phlex 2.2.2 → 2.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/lib/phlex/csv.rb +9 -11
- data/lib/phlex/fifo_cache_store.rb +1 -3
- data/lib/phlex/helpers.rb +2 -4
- data/lib/phlex/html.rb +1 -1
- data/lib/phlex/sgml/elements.rb +38 -0
- data/lib/phlex/sgml.rb +34 -61
- data/lib/phlex/svg.rb +1 -1
- data/lib/phlex/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 43c33e9802643a8519ff45f208bccd4d8f6430cd642187e75ca2c7a8dbba7ea2
|
|
4
|
+
data.tar.gz: 8963d6c803fe8b20fc7143a5ff3c447f99a420b0ceb8556e697e844b2782a9f5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cfd387e534c132acb663c28b3e5e1c28cba54251d1fc3ed1c1ceb649cd41825b243e6eb54eeb32f91934fc3493f8d473cd73f1d036995a97a09f2211b6b0ce61
|
|
7
|
+
data.tar.gz: 0b5f88d2d1f291719e61494a9027dd47e20a33eabe8987f01e4492ec0d16f01b1029177b5c8116402bf00f02f3d14f81f9604289ced1cd216cd05373f7db7220
|
data/lib/phlex/csv.rb
CHANGED
|
@@ -139,32 +139,30 @@ class Phlex::CSV
|
|
|
139
139
|
","
|
|
140
140
|
end
|
|
141
141
|
|
|
142
|
-
private
|
|
143
|
-
|
|
144
|
-
def column(header = nil, value)
|
|
142
|
+
private def column(header = nil, value)
|
|
145
143
|
@_row_buffer << [header, value]
|
|
146
144
|
end
|
|
147
145
|
|
|
148
|
-
def each_item(&)
|
|
146
|
+
private def each_item(&)
|
|
149
147
|
collection.each(&)
|
|
150
148
|
end
|
|
151
149
|
|
|
152
150
|
# Override and set to `false` to disable rendering headers.
|
|
153
|
-
def render_headers?
|
|
151
|
+
private def render_headers?
|
|
154
152
|
true
|
|
155
153
|
end
|
|
156
154
|
|
|
157
155
|
# Override and set to `true` to strip leading and trailing whitespace from values.
|
|
158
|
-
def trim_whitespace?
|
|
156
|
+
private def trim_whitespace?
|
|
159
157
|
false
|
|
160
158
|
end
|
|
161
159
|
|
|
162
160
|
# Override and set to `false` to disable CSV injection escapes or `true` to enable.
|
|
163
|
-
def escape_csv_injection?
|
|
161
|
+
private def escape_csv_injection?
|
|
164
162
|
UNDEFINED
|
|
165
163
|
end
|
|
166
164
|
|
|
167
|
-
def __escape__(buffer, value, escape_csv_injection:, strip_whitespace:, escape_regex:)
|
|
165
|
+
private def __escape__(buffer, value, escape_csv_injection:, strip_whitespace:, escape_regex:)
|
|
168
166
|
value = case value
|
|
169
167
|
when String
|
|
170
168
|
value
|
|
@@ -218,12 +216,12 @@ class Phlex::CSV
|
|
|
218
216
|
end
|
|
219
217
|
|
|
220
218
|
# Handle legacy `view_template` method
|
|
221
|
-
def respond_to_missing?(method_name, include_private)
|
|
219
|
+
private def respond_to_missing?(method_name, include_private)
|
|
222
220
|
(method_name == :row_template && respond_to?(:view_template)) || super
|
|
223
221
|
end
|
|
224
222
|
|
|
225
223
|
# Handle legacy `view_template` method
|
|
226
|
-
def method_missing(method_name, ...)
|
|
224
|
+
private def method_missing(method_name, ...)
|
|
227
225
|
if method_name == :row_template && respond_to?(:view_template)
|
|
228
226
|
warn "Deprecated: Use `row_template` instead of `view_template` in Phlex CSVs."
|
|
229
227
|
self.class.alias_method :row_template, :view_template
|
|
@@ -233,7 +231,7 @@ class Phlex::CSV
|
|
|
233
231
|
end
|
|
234
232
|
end
|
|
235
233
|
|
|
236
|
-
def ensure_escape_csv_injection_configured!
|
|
234
|
+
private def ensure_escape_csv_injection_configured!
|
|
237
235
|
if escape_csv_injection? == UNDEFINED
|
|
238
236
|
raise <<~MESSAGE
|
|
239
237
|
You need to define `escape_csv_injection?` in #{self.class.name}.
|
data/lib/phlex/helpers.rb
CHANGED
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# @api private
|
|
4
4
|
module Phlex::Helpers
|
|
5
|
-
private
|
|
6
|
-
|
|
7
|
-
def mix(*args)
|
|
5
|
+
private def mix(*args)
|
|
8
6
|
args.each_with_object({}) do |object, result|
|
|
9
7
|
result.merge!(object) do |_key, old, new|
|
|
10
8
|
case [old, new].freeze
|
|
@@ -43,7 +41,7 @@ module Phlex::Helpers
|
|
|
43
41
|
end
|
|
44
42
|
end
|
|
45
43
|
|
|
46
|
-
def grab(**bindings)
|
|
44
|
+
private def grab(**bindings)
|
|
47
45
|
if bindings.size > 1
|
|
48
46
|
bindings.values
|
|
49
47
|
else
|
data/lib/phlex/html.rb
CHANGED
|
@@ -55,7 +55,7 @@ class Phlex::HTML < Phlex::SGML
|
|
|
55
55
|
raise Phlex::ArgumentError.new("Expected the tag name to be a Symbol.")
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
if (tag = StandardElements.__registered_elements__[name]) || (
|
|
58
|
+
if (tag = StandardElements.__registered_elements__[name]) || (tag = name.name.tr("_", "-")).include?("-")
|
|
59
59
|
if attributes.length > 0 # with attributes
|
|
60
60
|
if block_given # with content block
|
|
61
61
|
buffer << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes)) << ">"
|
data/lib/phlex/sgml/elements.rb
CHANGED
|
@@ -1,6 +1,41 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Phlex::SGML::Elements
|
|
4
|
+
COMMA_SEPARATED_TOKENS = {
|
|
5
|
+
img: <<~RUBY,
|
|
6
|
+
if Array === (srcset_attribute = attributes[:srcset])
|
|
7
|
+
attributes[:srcset] = __nested_tokens__(srcset_attribute, ", ")
|
|
8
|
+
end
|
|
9
|
+
RUBY
|
|
10
|
+
link: <<~RUBY,
|
|
11
|
+
if Array === (media_attribute = attributes[:media])
|
|
12
|
+
attributes[:media] = __nested_tokens__(media_attribute, ", ")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
if Array === (sizes_attribute = attributes[:sizes])
|
|
16
|
+
attributes[:sizes] = __nested_tokens__(sizes_attribute, ", ")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
if Array === (imagesrcset_attribute = attributes[:imagesrcset])
|
|
20
|
+
rel_attribute = attributes[:rel] || attributes["rel"]
|
|
21
|
+
as_attribute = attributes[:as] || attributes["as"]
|
|
22
|
+
|
|
23
|
+
if ("preload" == rel_attribute || :preload == rel_attribute) && ("image" == as_attribute || :image == as_attribute)
|
|
24
|
+
attributes[:imagesrcset] = __nested_tokens__(imagesrcset_attribute, ", ")
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
RUBY
|
|
28
|
+
input: <<~RUBY,
|
|
29
|
+
if Array === (accept_attribute = attributes[:accept])
|
|
30
|
+
type_attribute = attributes[:type] || attributes["type"]
|
|
31
|
+
|
|
32
|
+
if "file" == type_attribute || :file == type_attribute
|
|
33
|
+
attributes[:accept] = __nested_tokens__(accept_attribute, ", ")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
RUBY
|
|
37
|
+
}.freeze
|
|
38
|
+
|
|
4
39
|
def __registered_elements__
|
|
5
40
|
@__registered_elements__ ||= {}
|
|
6
41
|
end
|
|
@@ -23,6 +58,7 @@ module Phlex::SGML::Elements
|
|
|
23
58
|
if block_given # with content block
|
|
24
59
|
buffer << "<#{tag}"
|
|
25
60
|
begin
|
|
61
|
+
#{COMMA_SEPARATED_TOKENS[method_name]}
|
|
26
62
|
buffer << (Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes))
|
|
27
63
|
ensure
|
|
28
64
|
buffer << ">"
|
|
@@ -53,6 +89,7 @@ module Phlex::SGML::Elements
|
|
|
53
89
|
else # without content
|
|
54
90
|
buffer << "<#{tag}"
|
|
55
91
|
begin
|
|
92
|
+
#{COMMA_SEPARATED_TOKENS[method_name]}
|
|
56
93
|
buffer << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes))
|
|
57
94
|
ensure
|
|
58
95
|
buffer << "></#{tag}>"
|
|
@@ -114,6 +151,7 @@ module Phlex::SGML::Elements
|
|
|
114
151
|
if attributes.length > 0 # with attributes
|
|
115
152
|
buffer << "<#{tag}"
|
|
116
153
|
begin
|
|
154
|
+
#{COMMA_SEPARATED_TOKENS[method_name]}
|
|
117
155
|
buffer << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes))
|
|
118
156
|
ensure
|
|
119
157
|
buffer << ">"
|
data/lib/phlex/sgml.rb
CHANGED
|
@@ -3,13 +3,7 @@
|
|
|
3
3
|
# **Standard Generalized Markup Language** for behaviour common to {HTML} and {SVG}.
|
|
4
4
|
class Phlex::SGML
|
|
5
5
|
UNSAFE_ATTRIBUTES = Set.new(%w[srcdoc sandbox http-equiv]).freeze
|
|
6
|
-
REF_ATTRIBUTES = Set.new(%w[href src action formaction lowsrc dynsrc background ping
|
|
7
|
-
NAMED_CHARACTER_REFERENCES = {
|
|
8
|
-
"colon" => ":",
|
|
9
|
-
"tab" => "\t",
|
|
10
|
-
"newline" => "\n",
|
|
11
|
-
}.freeze
|
|
12
|
-
UNSAFE_ATTRIBUTE_NAME_CHARS = %r([<>&"'/=\s\x00])
|
|
6
|
+
REF_ATTRIBUTES = Set.new(%w[href src action formaction lowsrc dynsrc background ping]).freeze
|
|
13
7
|
|
|
14
8
|
ERBCompiler = ERB::Compiler.new("<>").tap do |compiler|
|
|
15
9
|
compiler.pre_cmd = [""]
|
|
@@ -31,7 +25,7 @@ class Phlex::SGML
|
|
|
31
25
|
end
|
|
32
26
|
|
|
33
27
|
# Create a new instance of the component.
|
|
34
|
-
# @note The block will not be delegated {#initialize}. Instead, it will be sent to {#
|
|
28
|
+
# @note The block will not be delegated to {#initialize}. Instead, it will be sent to {#view_template} when rendering.
|
|
35
29
|
def new(*a, **k, &block)
|
|
36
30
|
if block
|
|
37
31
|
object = super(*a, **k, &nil)
|
|
@@ -290,12 +284,12 @@ class Phlex::SGML
|
|
|
290
284
|
location = caller_locations(1, 1)[0]
|
|
291
285
|
|
|
292
286
|
full_key = [
|
|
293
|
-
app_version_key,
|
|
287
|
+
app_version_key, # invalidates the key when deploying new code in case of changes
|
|
294
288
|
self.class.name, # prevents collisions between classes
|
|
295
289
|
(self.class.object_id if enable_cache_reloading?), # enables reloading
|
|
296
290
|
location.base_label, # prevents collisions between different methods
|
|
297
291
|
location.lineno, # prevents collisions between different lines
|
|
298
|
-
cache_key,
|
|
292
|
+
cache_key, # allows for custom cache keys
|
|
299
293
|
].freeze
|
|
300
294
|
|
|
301
295
|
low_level_cache(full_key, **, &content)
|
|
@@ -342,52 +336,50 @@ class Phlex::SGML
|
|
|
342
336
|
ERB::Util.json_escape(string)
|
|
343
337
|
end
|
|
344
338
|
|
|
345
|
-
private
|
|
346
|
-
|
|
347
339
|
# Override this method to use a different deployment key.
|
|
348
|
-
def app_version_key
|
|
340
|
+
private def app_version_key
|
|
349
341
|
Phlex::DEPLOYED_AT
|
|
350
342
|
end
|
|
351
343
|
|
|
352
344
|
# Override this method to use a different cache store.
|
|
353
|
-
def cache_store
|
|
345
|
+
private def cache_store
|
|
354
346
|
raise "Cache store not implemented."
|
|
355
347
|
end
|
|
356
348
|
|
|
357
|
-
def enable_cache_reloading?
|
|
349
|
+
private def enable_cache_reloading?
|
|
358
350
|
false
|
|
359
351
|
end
|
|
360
352
|
|
|
361
|
-
def vanish(...)
|
|
353
|
+
private def vanish(...)
|
|
362
354
|
capture(...)
|
|
363
355
|
nil
|
|
364
356
|
end
|
|
365
357
|
|
|
366
|
-
def render?
|
|
358
|
+
private def render?
|
|
367
359
|
true
|
|
368
360
|
end
|
|
369
361
|
|
|
370
|
-
def format_object(object)
|
|
362
|
+
private def format_object(object)
|
|
371
363
|
case object
|
|
372
364
|
when Float, Integer
|
|
373
365
|
object.to_s
|
|
374
366
|
end
|
|
375
367
|
end
|
|
376
368
|
|
|
377
|
-
def around_template
|
|
369
|
+
private def around_template
|
|
378
370
|
yield
|
|
379
371
|
nil
|
|
380
372
|
end
|
|
381
373
|
|
|
382
|
-
def before_template
|
|
374
|
+
private def before_template
|
|
383
375
|
nil
|
|
384
376
|
end
|
|
385
377
|
|
|
386
|
-
def after_template
|
|
378
|
+
private def after_template
|
|
387
379
|
nil
|
|
388
380
|
end
|
|
389
381
|
|
|
390
|
-
def __yield_content__
|
|
382
|
+
private def __yield_content__
|
|
391
383
|
return unless block_given?
|
|
392
384
|
|
|
393
385
|
buffer = @_state.buffer
|
|
@@ -399,7 +391,7 @@ class Phlex::SGML
|
|
|
399
391
|
nil
|
|
400
392
|
end
|
|
401
393
|
|
|
402
|
-
def __yield_content_with_no_yield_args__
|
|
394
|
+
private def __yield_content_with_no_yield_args__
|
|
403
395
|
return unless block_given?
|
|
404
396
|
|
|
405
397
|
buffer = @_state.buffer
|
|
@@ -411,7 +403,7 @@ class Phlex::SGML
|
|
|
411
403
|
nil
|
|
412
404
|
end
|
|
413
405
|
|
|
414
|
-
def __yield_content_with_args__(*a)
|
|
406
|
+
private def __yield_content_with_args__(*a)
|
|
415
407
|
return unless block_given?
|
|
416
408
|
|
|
417
409
|
buffer = @_state.buffer
|
|
@@ -423,7 +415,7 @@ class Phlex::SGML
|
|
|
423
415
|
nil
|
|
424
416
|
end
|
|
425
417
|
|
|
426
|
-
def __implicit_output__(content)
|
|
418
|
+
private def __implicit_output__(content)
|
|
427
419
|
state = @_state
|
|
428
420
|
return true unless state.should_render?
|
|
429
421
|
|
|
@@ -448,7 +440,7 @@ class Phlex::SGML
|
|
|
448
440
|
end
|
|
449
441
|
|
|
450
442
|
# same as __implicit_output__ but escapes even `safe` objects
|
|
451
|
-
def __text__(content)
|
|
443
|
+
private def __text__(content)
|
|
452
444
|
state = @_state
|
|
453
445
|
return true unless state.should_render?
|
|
454
446
|
|
|
@@ -470,7 +462,7 @@ class Phlex::SGML
|
|
|
470
462
|
true
|
|
471
463
|
end
|
|
472
464
|
|
|
473
|
-
def __attributes__(attributes, buffer = +"")
|
|
465
|
+
private def __attributes__(attributes, buffer = +"")
|
|
474
466
|
attributes.each do |k, v|
|
|
475
467
|
next unless v
|
|
476
468
|
|
|
@@ -489,6 +481,10 @@ class Phlex::SGML
|
|
|
489
481
|
v.name.tr("_", "-").gsub('"', """)
|
|
490
482
|
when Integer, Float
|
|
491
483
|
v.to_s
|
|
484
|
+
when Date
|
|
485
|
+
v.iso8601
|
|
486
|
+
when Time
|
|
487
|
+
v.respond_to?(:iso8601) ? v.iso8601 : v.strftime("%Y-%m-%dT%H:%M:%S%:z")
|
|
492
488
|
when Hash
|
|
493
489
|
case k
|
|
494
490
|
when :style
|
|
@@ -524,9 +520,7 @@ class Phlex::SGML
|
|
|
524
520
|
if value != true && REF_ATTRIBUTES.include?(normalized_name)
|
|
525
521
|
case value
|
|
526
522
|
when String
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
if decoded_value.downcase.delete("^a-z:").start_with?("javascript:")
|
|
523
|
+
if value.downcase.delete("^a-z:").start_with?("javascript:")
|
|
530
524
|
# We just ignore these because they were likely not specified by the developer.
|
|
531
525
|
next
|
|
532
526
|
end
|
|
@@ -544,7 +538,7 @@ class Phlex::SGML
|
|
|
544
538
|
end
|
|
545
539
|
end
|
|
546
540
|
|
|
547
|
-
if name.match?(
|
|
541
|
+
if name.match?(/[<>&"']/)
|
|
548
542
|
raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
|
|
549
543
|
end
|
|
550
544
|
|
|
@@ -565,7 +559,7 @@ class Phlex::SGML
|
|
|
565
559
|
|
|
566
560
|
# Provides the nested-attributes case for serializing out attributes.
|
|
567
561
|
# This allows us to skip many of the checks the `__attributes__` method must perform.
|
|
568
|
-
def __nested_attributes__(attributes, base_name, buffer = +"")
|
|
562
|
+
private def __nested_attributes__(attributes, base_name, buffer = +"")
|
|
569
563
|
attributes.each do |k, v|
|
|
570
564
|
next unless v
|
|
571
565
|
|
|
@@ -580,7 +574,7 @@ class Phlex::SGML
|
|
|
580
574
|
else raise Phlex::ArgumentError.new("Attribute keys should be Strings or Symbols")
|
|
581
575
|
end
|
|
582
576
|
|
|
583
|
-
if name.match?(
|
|
577
|
+
if name.match?(/[<>&"']/)
|
|
584
578
|
raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
|
|
585
579
|
end
|
|
586
580
|
end
|
|
@@ -614,7 +608,7 @@ class Phlex::SGML
|
|
|
614
608
|
end
|
|
615
609
|
end
|
|
616
610
|
|
|
617
|
-
def __nested_tokens__(tokens)
|
|
611
|
+
private def __nested_tokens__(tokens, sep = " ")
|
|
618
612
|
buffer = +""
|
|
619
613
|
|
|
620
614
|
i, length = 0, tokens.length
|
|
@@ -625,28 +619,28 @@ class Phlex::SGML
|
|
|
625
619
|
case token
|
|
626
620
|
when String
|
|
627
621
|
if i > 0
|
|
628
|
-
buffer <<
|
|
622
|
+
buffer << sep << token
|
|
629
623
|
else
|
|
630
624
|
buffer << token
|
|
631
625
|
end
|
|
632
626
|
when Symbol
|
|
633
627
|
if i > 0
|
|
634
|
-
buffer <<
|
|
628
|
+
buffer << sep << token.name.tr("_", "-")
|
|
635
629
|
else
|
|
636
630
|
buffer << token.name.tr("_", "-")
|
|
637
631
|
end
|
|
638
632
|
when Integer, Float, Phlex::SGML::SafeObject
|
|
639
633
|
if i > 0
|
|
640
|
-
buffer <<
|
|
634
|
+
buffer << sep << token.to_s
|
|
641
635
|
else
|
|
642
636
|
buffer << token.to_s
|
|
643
637
|
end
|
|
644
638
|
when Array
|
|
645
639
|
if token.length > 0
|
|
646
640
|
if i > 0
|
|
647
|
-
buffer <<
|
|
641
|
+
buffer << sep << __nested_tokens__(token, sep)
|
|
648
642
|
else
|
|
649
|
-
buffer << __nested_tokens__(token)
|
|
643
|
+
buffer << __nested_tokens__(token, sep)
|
|
650
644
|
end
|
|
651
645
|
end
|
|
652
646
|
when nil
|
|
@@ -661,29 +655,8 @@ class Phlex::SGML
|
|
|
661
655
|
buffer.gsub('"', """)
|
|
662
656
|
end
|
|
663
657
|
|
|
664
|
-
private def decode_html_character_references(value)
|
|
665
|
-
value
|
|
666
|
-
.gsub(/&#x([0-9a-f]+);?/i) {
|
|
667
|
-
begin
|
|
668
|
-
[$1.to_i(16)].pack("U*")
|
|
669
|
-
rescue
|
|
670
|
-
""
|
|
671
|
-
end
|
|
672
|
-
}
|
|
673
|
-
.gsub(/&#(\d+);?/) {
|
|
674
|
-
begin
|
|
675
|
-
[$1.to_i].pack("U*")
|
|
676
|
-
rescue
|
|
677
|
-
""
|
|
678
|
-
end
|
|
679
|
-
}
|
|
680
|
-
.gsub(/&([a-z][a-z0-9]+);?/i) {
|
|
681
|
-
NAMED_CHARACTER_REFERENCES[$1.downcase] || ""
|
|
682
|
-
}
|
|
683
|
-
end
|
|
684
|
-
|
|
685
658
|
# Result is **unsafe**, so it should be escaped!
|
|
686
|
-
def __styles__(styles)
|
|
659
|
+
private def __styles__(styles)
|
|
687
660
|
case styles
|
|
688
661
|
when Array, Set
|
|
689
662
|
styles.filter_map do |s|
|
data/lib/phlex/svg.rb
CHANGED
|
@@ -41,7 +41,7 @@ class Phlex::SVG < Phlex::SGML
|
|
|
41
41
|
raise Phlex::ArgumentError.new("Expected the tag name to be a Symbol.")
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
if (tag = StandardElements.__registered_elements__[name]) || (
|
|
44
|
+
if (tag = StandardElements.__registered_elements__[name]) || (tag = name.name.tr("_", "-")).include?("-")
|
|
45
45
|
if attributes.length > 0 # with attributes
|
|
46
46
|
if block_given # with content block
|
|
47
47
|
buffer << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes)) << ">"
|
data/lib/phlex/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: phlex
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.3.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:
|
|
11
|
+
date: 2025-05-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: zeitwerk
|