phlex 2.1.1 → 2.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e0de463dbf4f663fc2ac8dd8d0b0a080dc8d7bf90ba209d56b366b36634e9cc9
4
- data.tar.gz: ccad039d4962eb42076c562cb9f9617197f878a1860b13f66aae2b34f2686333
3
+ metadata.gz: d3eae91fcc5ec985b668a5e016c518f6392cd91d6d155a1f60552282755ca0e7
4
+ data.tar.gz: 4bac6d025488edbc104135dc3417286cc4782d4f022caff2a8a414afa12d09c3
5
5
  SHA512:
6
- metadata.gz: 959b8c7b1bdbbf281b0da7576257719da2ba077045e9ad1d673348d60a01dfa731b6b9b2e9031cce81e896f98a195478008d667e99bf471e50348bc6b978c393
7
- data.tar.gz: 93dab7d61efa8f2009698cb8ae7d12748b4cb1bfe5c2bf48e58c41e5c930f923789c00ff3c346e878107f9cbe280951e23f35fe5482c453799f5c7e81293715e
6
+ metadata.gz: 0a18175ffb32c8ae170f9fd2c7f4b990d39500cef81db66545c057f7ff6635a215ba257130c3715a82d1322666ec89badc9beb06fae3325d864a1630f685bea2
7
+ data.tar.gz: 4ee950b03d2d8f0b8efe085207ca42b139775dff4cc0a564661648995ef32f01fce22e84537194a7c37a6218031d9ab8a4ab7689b92bfeac53534aee4d2fc919
data/lib/phlex/csv.rb CHANGED
@@ -13,6 +13,7 @@ class Phlex::CSV
13
13
  @collection = collection
14
14
  @_row_buffer = []
15
15
  @_headers = []
16
+ @_row_appender = nil
16
17
  end
17
18
 
18
19
  attr_reader :collection
@@ -49,13 +50,7 @@ class Phlex::CSV
49
50
  MESSAGE
50
51
  end
51
52
 
52
- each_item do |record|
53
- if has_yielder
54
- yielder(record) { |*a, **k| row = row_template(*a, **k) }
55
- else
56
- around_row(record)
57
- end
58
-
53
+ row_appender = -> {
59
54
  row = row_buffer
60
55
 
61
56
  if first_row
@@ -108,13 +103,28 @@ class Phlex::CSV
108
103
  buffer << "\n"
109
104
 
110
105
  row_buffer.clear
106
+ }
107
+
108
+ if has_yielder
109
+ each_item do |record|
110
+ yielder(record) do |*a, **k|
111
+ row_template(*a, **k)
112
+ row_appender.call
113
+ end
114
+ end
115
+ else
116
+ @row_appender = row_appender
117
+ each_item do |record|
118
+ around_row(record)
119
+ end
111
120
  end
112
121
 
113
122
  buffer
114
123
  end
115
124
 
116
- def around_row(item)
117
- row_template(item)
125
+ def around_row(...)
126
+ row_template(...)
127
+ @row_appender.call
118
128
  end
119
129
 
120
130
  def filename
@@ -168,7 +178,9 @@ class Phlex::CSV
168
178
  value = value.strip
169
179
 
170
180
  if escape_csv_injection
171
- if FORMULA_PREFIXES_MAP[value.getbyte(0)]
181
+ if value.empty?
182
+ buffer << value
183
+ elsif FORMULA_PREFIXES_MAP[value.getbyte(0)]
172
184
  value.gsub!('"', '""')
173
185
  buffer << '"\'' << value << '"'
174
186
  elsif value.match?(escape_regex)
@@ -184,7 +196,9 @@ class Phlex::CSV
184
196
  if escape_csv_injection
185
197
  first_byte = value.getbyte(0)
186
198
 
187
- if FORMULA_PREFIXES_MAP[first_byte]
199
+ if value.empty?
200
+ buffer << '""'
201
+ elsif FORMULA_PREFIXES_MAP[first_byte]
188
202
  buffer << '"\'' << value.gsub('"', '""') << '"'
189
203
  elsif value.match?(escape_regex)
190
204
  buffer << '"' << value.gsub('"', '""') << '"'
@@ -192,7 +206,9 @@ class Phlex::CSV
192
206
  buffer << value
193
207
  end
194
208
  else # not escaping CSV injection
195
- if value.match?(escape_regex)
209
+ if value.empty?
210
+ buffer << '""'
211
+ elsif value.match?(escape_regex)
196
212
  buffer << '"' << value.gsub('"', '""') << '"'
197
213
  else
198
214
  buffer << value
data/lib/phlex/helpers.rb CHANGED
@@ -26,6 +26,10 @@ module Phlex::Helpers
26
26
  [old] + new.to_a
27
27
  in [String, String]
28
28
  "#{old} #{new}"
29
+ in [_, Hash]
30
+ { _: old, **new }
31
+ in [Hash, _]
32
+ { **old, _: new }
29
33
  in [_, nil]
30
34
  old
31
35
  else
@@ -472,6 +472,13 @@ module Phlex::HTML::StandardElements
472
472
  # [Spec](https://html.spec.whatwg.org/#the-select-element)
473
473
  register_element def select(**attributes, &content) = nil
474
474
 
475
+ # [EXPERIMENTAL] Outputs a `<selectedcontent>` tag.
476
+ #
477
+ # [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/selectedcontent)
478
+ # [Draft Spec](https://github.com/whatwg/html/pull/10633)
479
+ # [Can I Use?](https://caniuse.com/mdn-html_elements_selectedcontent)
480
+ register_element def selectedcontent(**attributes, &content) = nil
481
+
475
482
  # Outputs a `<slot>` tag.
476
483
  #
477
484
  # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/slot)
data/lib/phlex/html.rb CHANGED
@@ -1,9 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Phlex::HTML < Phlex::SGML
4
- autoload :StandardElements, "phlex/html/standard_elements"
5
- autoload :VoidElements, "phlex/html/void_elements"
6
-
7
4
  extend Phlex::SGML::Elements
8
5
  include VoidElements, StandardElements
9
6
 
@@ -14,16 +14,7 @@ class Phlex::SGML::State
14
14
 
15
15
  attr_accessor :capturing, :user_context
16
16
 
17
- attr_reader :fragments, :fragment_depth, :output_buffer
18
-
19
- def buffer
20
- case @buffer
21
- when Proc
22
- @buffer.call
23
- else
24
- @buffer
25
- end
26
- end
17
+ attr_reader :fragments, :fragment_depth, :output_buffer, :buffer
27
18
 
28
19
  def around_render(component)
29
20
  stack = @stack
@@ -85,17 +76,23 @@ class Phlex::SGML::State
85
76
  end
86
77
 
87
78
  def caching(&)
88
- buffer = +""
89
- @cache_stack.push([buffer, {}].freeze)
90
- capturing_into(buffer, &)
91
- @cache_stack.pop
79
+ result = nil
80
+
81
+ capture do
82
+ @cache_stack.push([buffer, {}].freeze)
83
+ yield
84
+ result = @cache_stack.pop
85
+ end
86
+
87
+ result
92
88
  end
93
89
 
94
90
  def caching?
95
91
  @cache_stack.length > 0
96
92
  end
97
93
 
98
- def capturing_into(new_buffer)
94
+ def capture
95
+ new_buffer = +""
99
96
  original_buffer = @buffer
100
97
  original_capturing = @capturing
101
98
  original_fragments = @fragments
data/lib/phlex/sgml.rb CHANGED
@@ -16,11 +16,6 @@ class Phlex::SGML
16
16
  end
17
17
  end
18
18
 
19
- autoload :Elements, "phlex/sgml/elements"
20
- autoload :SafeObject, "phlex/sgml/safe_object"
21
- autoload :SafeValue, "phlex/sgml/safe_value"
22
- autoload :State, "phlex/sgml/state"
23
-
24
19
  include Phlex::Helpers
25
20
 
26
21
  class << self
@@ -217,9 +212,9 @@ class Phlex::SGML
217
212
  return "" unless block
218
213
 
219
214
  if args.length > 0
220
- @_state.capturing_into(+"") { __yield_content_with_args__(*args, &block) }
215
+ @_state.capture { __yield_content_with_args__(*args, &block) }
221
216
  else
222
- @_state.capturing_into(+"") { __yield_content__(&block) }
217
+ @_state.capture { __yield_content__(&block) }
223
218
  end
224
219
  end
225
220
 
@@ -298,6 +293,8 @@ class Phlex::SGML
298
293
  ].freeze
299
294
 
300
295
  low_level_cache(full_key, **, &content)
296
+
297
+ nil
301
298
  end
302
299
 
303
300
  # Cache a block of content where you control the entire cache key.
@@ -331,6 +328,8 @@ class Phlex::SGML
331
328
  end
332
329
  end
333
330
  end
331
+
332
+ nil
334
333
  end
335
334
 
336
335
  def json_escape(string)
@@ -353,15 +352,8 @@ class Phlex::SGML
353
352
  false
354
353
  end
355
354
 
356
- def vanish(*args)
357
- return unless block_given?
358
-
359
- if args.length > 0
360
- @_state.capturing_into(Phlex::Vanish) { yield(*args) }
361
- else
362
- @_state.capturing_into(Phlex::Vanish) { yield(self) }
363
- end
364
-
355
+ def vanish(...)
356
+ capture(...)
365
357
  nil
366
358
  end
367
359
 
@@ -569,14 +561,20 @@ class Phlex::SGML
569
561
  attributes.each do |k, v|
570
562
  next unless v
571
563
 
572
- name = case k
573
- when String then k
574
- when Symbol then k.name.tr("_", "-")
575
- else raise Phlex::ArgumentError.new("Attribute keys should be Strings or Symbols")
576
- end
564
+ if (root_key = (:_ == k))
565
+ name = ""
566
+ original_base_name = base_name
567
+ base_name = base_name.delete_suffix("-")
568
+ else
569
+ name = case k
570
+ when String then k
571
+ when Symbol then k.name.tr("_", "-")
572
+ else raise Phlex::ArgumentError.new("Attribute keys should be Strings or Symbols")
573
+ end
577
574
 
578
- if name.match?(/[<>&"']/)
579
- raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
575
+ if name.match?(/[<>&"']/)
576
+ raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
577
+ end
580
578
  end
581
579
 
582
580
  case v
@@ -600,6 +598,10 @@ class Phlex::SGML
600
598
  raise Phlex::ArgumentError.new("Invalid attribute value #{v.inspect}.")
601
599
  end
602
600
 
601
+ if root_key
602
+ base_name = original_base_name
603
+ end
604
+
603
605
  buffer
604
606
  end
605
607
  end
data/lib/phlex/svg.rb CHANGED
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Phlex::SVG < Phlex::SGML
4
- autoload :StandardElements, "phlex/svg/standard_elements"
5
-
6
4
  include StandardElements
7
5
 
8
6
  # Returns the string "image/svg+xml"
data/lib/phlex/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Phlex
4
- VERSION = "2.1.1"
4
+ VERSION = "2.2.0"
5
5
  end
data/lib/phlex.rb CHANGED
@@ -2,26 +2,22 @@
2
2
 
3
3
  require "erb"
4
4
  require "set"
5
+ require "zeitwerk"
5
6
 
6
7
  module Phlex
7
- autoload :VERSION, "phlex/version"
8
-
9
- autoload :Kit, "phlex/kit"
10
- autoload :FIFO, "phlex/fifo"
11
- autoload :Vanish, "phlex/vanish"
12
- autoload :Helpers, "phlex/helpers"
13
- autoload :FIFOCacheStore, "phlex/fifo_cache_store"
14
-
15
- autoload :CSV, "phlex/csv"
16
- autoload :SVG, "phlex/svg"
17
- autoload :HTML, "phlex/html"
18
- autoload :SGML, "phlex/sgml"
19
-
20
- autoload :Error, "phlex/error"
21
- autoload :NameError, "phlex/errors/name_error"
22
- autoload :RuntimeError, "phlex/errors/runtime_error"
23
- autoload :ArgumentError, "phlex/errors/argument_error"
24
- autoload :DoubleRenderError, "phlex/errors/double_render_error"
8
+ Loader = Zeitwerk::Loader.for_gem.tap do |loader|
9
+ loader.push_dir("lib/phlex/errors", namespace: Phlex)
10
+ loader.inflector.inflect(
11
+ "csv" => "CSV",
12
+ "fifo" => "FIFO",
13
+ "fifo_cache_store" => "FIFOCacheStore",
14
+ "html" => "HTML",
15
+ "sgml" => "SGML",
16
+ "svg" => "SVG",
17
+ )
18
+
19
+ loader.setup
20
+ end
25
21
 
26
22
  Escape = ERB::Escape
27
23
 
@@ -36,17 +32,6 @@ module Phlex
36
32
  end
37
33
  end
38
34
 
39
- def self.eager_load
40
- queue = [self]
41
-
42
- while (mod = queue.shift)
43
- mod.constants.each do |const_name|
44
- const = mod.const_get(const_name)
45
- queue << const if Module === const
46
- end
47
- end
48
- end
49
-
50
35
  # Generate an HTML string using Phlex’ HTML DSL
51
36
  def self.html(&block)
52
37
  HTML.call do |component|
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phlex
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Drapper
8
8
  - Will Cosgrove
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-03-07 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2025-04-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: zeitwerk
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  description: Build HTML, SVG and CSV views with Ruby classes.
14
28
  email:
15
29
  - joel@drapper.me
@@ -40,7 +54,6 @@ files:
40
54
  - lib/phlex/sgml/state.rb
41
55
  - lib/phlex/svg.rb
42
56
  - lib/phlex/svg/standard_elements.rb
43
- - lib/phlex/vanish.rb
44
57
  - lib/phlex/version.rb
45
58
  homepage: https://www.phlex.fun
46
59
  licenses:
@@ -65,7 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
65
78
  - !ruby/object:Gem::Version
66
79
  version: '0'
67
80
  requirements: []
68
- rubygems_version: 3.6.2
81
+ rubygems_version: 3.6.6
69
82
  specification_version: 4
70
83
  summary: Object-oriented views in Ruby.
71
84
  test_files: []
data/lib/phlex/vanish.rb DELETED
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # @api private
4
- module Phlex::Vanish
5
- extend self
6
-
7
- def <<(anything)
8
- self
9
- end
10
-
11
- def bytesize
12
- 0
13
- end
14
-
15
- def dup
16
- self
17
- end
18
-
19
- def clear
20
- self
21
- end
22
- end