phlex 2.3.1 → 2.4.0.beta2

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.
@@ -0,0 +1,280 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlex::SGML::Attributes
4
+ extend self
5
+
6
+ UNSAFE_ATTRIBUTES = Set.new(%w[srcdoc sandbox http-equiv]).freeze
7
+ REF_ATTRIBUTES = Set.new(%w[href src action formaction lowsrc dynsrc background ping]).freeze
8
+
9
+ def generate_attributes(attributes, buffer = +"")
10
+ attributes.each do |k, v|
11
+ next unless v
12
+
13
+ name = case k
14
+ when String then k
15
+ when Symbol then k.name.tr("_", "-")
16
+ else raise Phlex::ArgumentError.new("Attribute keys should be Strings or Symbols.")
17
+ end
18
+
19
+ value = case v
20
+ when true
21
+ true
22
+ when String
23
+ v.gsub('"', """)
24
+ when Symbol
25
+ v.name.tr("_", "-").gsub('"', """)
26
+ when Integer, Float
27
+ v.to_s
28
+ when Date
29
+ v.iso8601
30
+ when Time
31
+ v.respond_to?(:iso8601) ? v.iso8601 : v.strftime("%Y-%m-%dT%H:%M:%S%:z")
32
+ when Hash
33
+ case k
34
+ when :style
35
+ generate_styles(v).gsub('"', """)
36
+ else
37
+ generate_nested_attributes(v, "#{name}-", buffer)
38
+ end
39
+ when Array
40
+ case k
41
+ when :style
42
+ generate_styles(v).gsub('"', """)
43
+ else
44
+ generate_nested_tokens(v)
45
+ end
46
+ when Set
47
+ case k
48
+ when :style
49
+ generate_styles(v).gsub('"', """)
50
+ else
51
+ generate_nested_tokens(v.to_a)
52
+ end
53
+ when Phlex::SGML::SafeObject
54
+ v.to_s.gsub('"', """)
55
+ else
56
+ raise Phlex::ArgumentError.new("Invalid attribute value for #{k}: #{v.inspect}.")
57
+ end
58
+
59
+ lower_name = name.downcase
60
+
61
+ unless Phlex::SGML::SafeObject === v
62
+ normalized_name = lower_name.delete("^a-z-")
63
+
64
+ if value != true && REF_ATTRIBUTES.include?(normalized_name)
65
+ case value
66
+ when String
67
+ if value.downcase.delete("^a-z:").start_with?("javascript:")
68
+ # We just ignore these because they were likely not specified by the developer.
69
+ next
70
+ end
71
+ else
72
+ raise Phlex::ArgumentError.new("Invalid attribute value for #{k}: #{v.inspect}.")
73
+ end
74
+ end
75
+
76
+ if normalized_name.bytesize > 2 && normalized_name.start_with?("on") && !normalized_name.include?("-")
77
+ raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
78
+ end
79
+
80
+ if UNSAFE_ATTRIBUTES.include?(normalized_name)
81
+ raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
82
+ end
83
+ end
84
+
85
+ if name.match?(/[<>&"']/)
86
+ raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
87
+ end
88
+
89
+ if lower_name.to_sym == :id && k != :id
90
+ raise Phlex::ArgumentError.new(":id attribute should only be passed as a lowercase symbol.")
91
+ end
92
+
93
+ case value
94
+ when true
95
+ buffer << " " << name
96
+ when String
97
+ buffer << " " << name << '="' << value << '"'
98
+ end
99
+ end
100
+
101
+ buffer
102
+ end
103
+
104
+ # Provides the nested-attributes case for serializing out attributes.
105
+ # This allows us to skip many of the checks the `__attributes__` method must perform.
106
+ def generate_nested_attributes(attributes, base_name, buffer = +"")
107
+ attributes.each do |k, v|
108
+ next unless v
109
+
110
+ if (root_key = (:_ == k))
111
+ name = ""
112
+ original_base_name = base_name
113
+ base_name = base_name.delete_suffix("-")
114
+ else
115
+ name = case k
116
+ when String then k
117
+ when Symbol then k.name.tr("_", "-")
118
+ else raise Phlex::ArgumentError.new("Attribute keys should be Strings or Symbols")
119
+ end
120
+
121
+ if name.match?(/[<>&"']/)
122
+ raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
123
+ end
124
+ end
125
+
126
+ case v
127
+ when true
128
+ buffer << " " << base_name << name
129
+ when String
130
+ buffer << " " << base_name << name << '="' << v.gsub('"', "&quot;") << '"'
131
+ when Symbol
132
+ buffer << " " << base_name << name << '="' << v.name.tr("_", "-").gsub('"', "&quot;") << '"'
133
+ when Integer, Float
134
+ buffer << " " << base_name << name << '="' << v.to_s << '"'
135
+ when Hash
136
+ generate_nested_attributes(v, "#{base_name}#{name}-", buffer)
137
+ when Array
138
+ if (value = generate_nested_tokens(v))
139
+ buffer << " " << base_name << name << '="' << value << '"'
140
+ end
141
+ when Set
142
+ if (value = generate_nested_tokens(v.to_a))
143
+ buffer << " " << base_name << name << '="' << value << '"'
144
+ end
145
+ when Phlex::SGML::SafeObject
146
+ buffer << " " << base_name << name << '="' << v.to_s.gsub('"', "&quot;") << '"'
147
+ else
148
+ raise Phlex::ArgumentError.new("Invalid attribute value #{v.inspect}.")
149
+ end
150
+
151
+ if root_key
152
+ base_name = original_base_name
153
+ end
154
+
155
+ buffer
156
+ end
157
+ end
158
+
159
+ def generate_nested_tokens(tokens, sep = " ", gsub_from = nil, gsub_to = "")
160
+ buffer = +""
161
+
162
+ i, length = 0, tokens.length
163
+
164
+ while i < length
165
+ token = tokens[i]
166
+
167
+ case token
168
+ when String
169
+ token = token.gsub(gsub_from, gsub_to) if gsub_from
170
+ if i > 0
171
+ buffer << sep << token
172
+ else
173
+ buffer << token
174
+ end
175
+ when Symbol
176
+ if i > 0
177
+ buffer << sep << token.name.tr("_", "-")
178
+ else
179
+ buffer << token.name.tr("_", "-")
180
+ end
181
+ when Integer, Float, Phlex::SGML::SafeObject
182
+ if i > 0
183
+ buffer << sep << token.to_s
184
+ else
185
+ buffer << token.to_s
186
+ end
187
+ when Array
188
+ if token.length > 0 && (value = generate_nested_tokens(token, sep, gsub_from, gsub_to))
189
+ if i > 0
190
+ buffer << sep << value
191
+ else
192
+ buffer << value
193
+ end
194
+ end
195
+ when Set
196
+ if token.length > 0 && (value = generate_nested_tokens(token.to_a, sep, gsub_from, gsub_to))
197
+ if i > 0
198
+ buffer << sep << value
199
+ else
200
+ buffer << value
201
+ end
202
+ end
203
+ when nil
204
+ # Do nothing
205
+ else
206
+ raise Phlex::ArgumentError.new("Invalid token type: #{token.class}.")
207
+ end
208
+
209
+ i += 1
210
+ end
211
+
212
+ return if buffer.empty?
213
+
214
+ buffer.gsub('"', "&quot;")
215
+ end
216
+
217
+ # The result is unsafe so should be escaped.
218
+ def generate_styles(styles)
219
+ case styles
220
+ when Array, Set
221
+ styles.filter_map do |s|
222
+ case s
223
+ when String
224
+ if s == "" || s.end_with?(";")
225
+ s
226
+ else
227
+ "#{s};"
228
+ end
229
+ when Phlex::SGML::SafeObject
230
+ value = s.to_s
231
+ value.end_with?(";") ? value : "#{value};"
232
+ when Hash
233
+ next generate_styles(s)
234
+ when nil
235
+ next nil
236
+ else
237
+ raise Phlex::ArgumentError.new("Invalid style: #{s.inspect}.")
238
+ end
239
+ end.join(" ")
240
+ when Hash
241
+ buffer = +""
242
+ i = 0
243
+ styles.each do |k, v|
244
+ prop = case k
245
+ when String
246
+ k
247
+ when Symbol
248
+ k.name.tr("_", "-")
249
+ else
250
+ raise Phlex::ArgumentError.new("Style keys should be Strings or Symbols.")
251
+ end
252
+
253
+ value = case v
254
+ when String
255
+ v
256
+ when Symbol
257
+ v.name.tr("_", "-")
258
+ when Integer, Float, Phlex::SGML::SafeObject
259
+ v.to_s
260
+ when nil
261
+ nil
262
+ else
263
+ raise Phlex::ArgumentError.new("Invalid style value: #{v.inspect}")
264
+ end
265
+
266
+ if value
267
+ if i == 0
268
+ buffer << prop << ": " << value << ";"
269
+ else
270
+ buffer << " " << prop << ": " << value << ";"
271
+ end
272
+ end
273
+
274
+ i += 1
275
+ end
276
+
277
+ buffer
278
+ end
279
+ end
280
+ end
@@ -4,16 +4,16 @@ module Phlex::SGML::Elements
4
4
  COMMA_SEPARATED_TOKENS = {
5
5
  img: <<~RUBY,
6
6
  if Array === (srcset_attribute = attributes[:srcset])
7
- attributes[:srcset] = __nested_tokens__(srcset_attribute, ", ")
7
+ attributes[:srcset] = Phlex::SGML::Attributes.generate_nested_tokens(srcset_attribute, ", ", ",", "%2C")
8
8
  end
9
9
  RUBY
10
10
  link: <<~RUBY,
11
11
  if Array === (media_attribute = attributes[:media])
12
- attributes[:media] = __nested_tokens__(media_attribute, ", ")
12
+ attributes[:media] = Phlex::SGML::Attributes.generate_nested_tokens(media_attribute, ", ", ",", "%2C")
13
13
  end
14
14
 
15
15
  if Array === (sizes_attribute = attributes[:sizes])
16
- attributes[:sizes] = __nested_tokens__(sizes_attribute, ", ")
16
+ attributes[:sizes] = Phlex::SGML::Attributes.generate_nested_tokens(sizes_attribute, ", ", ",", "%2C")
17
17
  end
18
18
 
19
19
  if Array === (imagesrcset_attribute = attributes[:imagesrcset])
@@ -21,7 +21,7 @@ module Phlex::SGML::Elements
21
21
  as_attribute = attributes[:as] || attributes["as"]
22
22
 
23
23
  if ("preload" == rel_attribute || :preload == rel_attribute) && ("image" == as_attribute || :image == as_attribute)
24
- attributes[:imagesrcset] = __nested_tokens__(imagesrcset_attribute, ", ")
24
+ attributes[:imagesrcset] = Phlex::SGML::Attributes.generate_nested_tokens(imagesrcset_attribute, ", ", ",", "%2C")
25
25
  end
26
26
  end
27
27
  RUBY
@@ -30,7 +30,7 @@ module Phlex::SGML::Elements
30
30
  type_attribute = attributes[:type] || attributes["type"]
31
31
 
32
32
  if "file" == type_attribute || :file == type_attribute
33
- attributes[:accept] = __nested_tokens__(accept_attribute, ", ")
33
+ attributes[:accept] = Phlex::SGML::Attributes.generate_nested_tokens(accept_attribute, ", ", ",", "%2C")
34
34
  end
35
35
  end
36
36
  RUBY
@@ -59,7 +59,7 @@ module Phlex::SGML::Elements
59
59
  buffer << "<#{tag}"
60
60
  begin
61
61
  #{COMMA_SEPARATED_TOKENS[method_name]}
62
- buffer << (Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes))
62
+ buffer << (Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes))
63
63
  ensure
64
64
  buffer << ">"
65
65
  end
@@ -90,7 +90,7 @@ module Phlex::SGML::Elements
90
90
  buffer << "<#{tag}"
91
91
  begin
92
92
  #{COMMA_SEPARATED_TOKENS[method_name]}
93
- buffer << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes))
93
+ buffer << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes))
94
94
  ensure
95
95
  buffer << "></#{tag}>"
96
96
  end
@@ -152,7 +152,7 @@ module Phlex::SGML::Elements
152
152
  buffer << "<#{tag}"
153
153
  begin
154
154
  #{COMMA_SEPARATED_TOKENS[method_name]}
155
- buffer << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes))
155
+ buffer << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes))
156
156
  ensure
157
157
  buffer << ">"
158
158
  end
data/lib/phlex/sgml.rb CHANGED
@@ -2,9 +2,6 @@
2
2
 
3
3
  # **Standard Generalized Markup Language** for behaviour common to {HTML} and {SVG}.
4
4
  class Phlex::SGML
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]).freeze
7
-
8
5
  ERBCompiler = ERB::Compiler.new("<>").tap do |compiler|
9
6
  compiler.pre_cmd = [""]
10
7
  compiler.put_cmd = "@_state.buffer.<<"
@@ -462,272 +459,40 @@ class Phlex::SGML
462
459
  true
463
460
  end
464
461
 
465
- private def __attributes__(attributes, buffer = +"")
466
- attributes.each do |k, v|
467
- next unless v
468
-
469
- name = case k
470
- when String then k
471
- when Symbol then k.name.tr("_", "-")
472
- else raise Phlex::ArgumentError.new("Attribute keys should be Strings or Symbols.")
473
- end
474
-
475
- value = case v
476
- when true
477
- true
478
- when String
479
- v.gsub('"', "&quot;")
480
- when Symbol
481
- v.name.tr("_", "-").gsub('"', "&quot;")
482
- when Integer, Float
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")
488
- when Hash
489
- case k
490
- when :style
491
- __styles__(v).gsub('"', "&quot;")
492
- else
493
- __nested_attributes__(v, "#{name}-", buffer)
494
- end
495
- when Array
496
- case k
497
- when :style
498
- __styles__(v).gsub('"', "&quot;")
499
- else
500
- __nested_tokens__(v)
501
- end
502
- when Set
503
- case k
504
- when :style
505
- __styles__(v).gsub('"', "&quot;")
506
- else
507
- __nested_tokens__(v.to_a)
508
- end
509
- when Phlex::SGML::SafeObject
510
- v.to_s.gsub('"', "&quot;")
511
- else
512
- raise Phlex::ArgumentError.new("Invalid attribute value for #{k}: #{v.inspect}.")
513
- end
514
-
515
- lower_name = name.downcase
516
-
517
- unless Phlex::SGML::SafeObject === v
518
- normalized_name = lower_name.delete("^a-z-")
519
-
520
- if value != true && REF_ATTRIBUTES.include?(normalized_name)
521
- case value
522
- when String
523
- if value.downcase.delete("^a-z:").start_with?("javascript:")
524
- # We just ignore these because they were likely not specified by the developer.
525
- next
526
- end
527
- else
528
- raise Phlex::ArgumentError.new("Invalid attribute value for #{k}: #{v.inspect}.")
529
- end
530
- end
531
-
532
- if normalized_name.bytesize > 2 && normalized_name.start_with?("on") && !normalized_name.include?("-")
533
- raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
534
- end
535
-
536
- if UNSAFE_ATTRIBUTES.include?(normalized_name)
537
- raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
538
- end
539
- end
540
-
541
- if name.match?(/[<>&"']/)
542
- raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
543
- end
544
-
545
- if lower_name.to_sym == :id && k != :id
546
- raise Phlex::ArgumentError.new(":id attribute should only be passed as a lowercase symbol.")
547
- end
548
-
549
- case value
550
- when true
551
- buffer << " " << name
552
- when String
553
- buffer << " " << name << '="' << value << '"'
554
- end
555
- end
556
-
557
- buffer
558
- end
559
-
560
- # Provides the nested-attributes case for serializing out attributes.
561
- # This allows us to skip many of the checks the `__attributes__` method must perform.
562
- private def __nested_attributes__(attributes, base_name, buffer = +"")
563
- attributes.each do |k, v|
564
- next unless v
565
-
566
- if (root_key = (:_ == k))
567
- name = ""
568
- original_base_name = base_name
569
- base_name = base_name.delete_suffix("-")
570
- else
571
- name = case k
572
- when String then k
573
- when Symbol then k.name.tr("_", "-")
574
- else raise Phlex::ArgumentError.new("Attribute keys should be Strings or Symbols")
575
- end
576
-
577
- if name.match?(/[<>&"']/)
578
- raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
579
- end
580
- end
581
-
582
- case v
583
- when true
584
- buffer << " " << base_name << name
585
- when String
586
- buffer << " " << base_name << name << '="' << v.gsub('"', "&quot;") << '"'
587
- when Symbol
588
- buffer << " " << base_name << name << '="' << v.name.tr("_", "-").gsub('"', "&quot;") << '"'
589
- when Integer, Float
590
- buffer << " " << base_name << name << '="' << v.to_s << '"'
591
- when Hash
592
- __nested_attributes__(v, "#{base_name}#{name}-", buffer)
593
- when Array
594
- buffer << " " << base_name << name << '="' << __nested_tokens__(v) << '"'
595
- when Set
596
- buffer << " " << base_name << name << '="' << __nested_tokens__(v.to_a) << '"'
597
- when Phlex::SGML::SafeObject
598
- buffer << " " << base_name << name << '="' << v.to_s.gsub('"', "&quot;") << '"'
599
- else
600
- raise Phlex::ArgumentError.new("Invalid attribute value #{v.inspect}.")
601
- end
602
-
603
- if root_key
604
- base_name = original_base_name
605
- end
606
-
607
- buffer
608
- end
462
+ private def __render_attributes__(attributes)
463
+ state = @_state
464
+ return unless state.should_render?
465
+ state.buffer << (Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes))
609
466
  end
610
467
 
611
- private def __nested_tokens__(tokens, sep = " ")
612
- buffer = +""
613
-
614
- i, length = 0, tokens.length
615
-
616
- while i < length
617
- token = tokens[i]
468
+ private_class_method def self.method_added(method_name)
469
+ if method_name == :view_template
470
+ location = instance_method(method_name).source_location[0]
618
471
 
619
- case token
620
- when String
621
- if i > 0
622
- buffer << sep << token
623
- else
624
- buffer << token
625
- end
626
- when Symbol
627
- if i > 0
628
- buffer << sep << token.name.tr("_", "-")
629
- else
630
- buffer << token.name.tr("_", "-")
631
- end
632
- when Integer, Float, Phlex::SGML::SafeObject
633
- if i > 0
634
- buffer << sep << token.to_s
635
- else
636
- buffer << token.to_s
637
- end
638
- when Array
639
- if token.length > 0
640
- if i > 0
641
- buffer << sep << __nested_tokens__(token, sep)
642
- else
643
- buffer << __nested_tokens__(token, sep)
644
- end
645
- end
646
- when nil
647
- # Do nothing
648
- else
649
- raise Phlex::ArgumentError.new("Invalid token type: #{token.class}.")
472
+ if location[0] in "/" | "."
473
+ Phlex.__expand_attribute_cache__(location)
650
474
  end
651
-
652
- i += 1
653
475
  end
654
476
 
655
- buffer.gsub('"', "&quot;")
477
+ super
656
478
  end
657
479
 
658
- # Result is **unsafe**, so it should be escaped!
659
- private def __styles__(styles)
660
- case styles
661
- when Array, Set
662
- styles.filter_map do |s|
663
- case s
664
- when String
665
- if s == "" || s.end_with?(";")
666
- s
667
- else
668
- "#{s};"
669
- end
670
- when Phlex::SGML::SafeObject
671
- value = s.to_s
672
- value.end_with?(";") ? value : "#{value};"
673
- when Hash
674
- next __styles__(s)
675
- when nil
676
- next nil
677
- else
678
- raise Phlex::ArgumentError.new("Invalid style: #{s.inspect}.")
679
- end
680
- end.join(" ")
681
- when Hash
682
- buffer = +""
683
- i = 0
684
- styles.each do |k, v|
685
- prop = case k
686
- when String
687
- k
688
- when Symbol
689
- k.name.tr("_", "-")
690
- else
691
- raise Phlex::ArgumentError.new("Style keys should be Strings or Symbols.")
692
- end
480
+ def self.__compile__(method_name)
481
+ path, line = instance_method(method_name).source_location
482
+ Phlex::Compiler::Method.new(self, path, line, method_name).compile
483
+ end
693
484
 
694
- value = case v
695
- when String
696
- v
697
- when Symbol
698
- v.name.tr("_", "-")
699
- when Integer, Float, Phlex::SGML::SafeObject
700
- v.to_s
701
- when nil
702
- nil
485
+ def __map_exception__(exception)
486
+ exception.set_backtrace(
487
+ exception.backtrace_locations.map do |loc|
488
+ if ((map = Phlex::Compiler::MAP[loc.path]) && (line = map[loc.lineno]))
489
+ "[Phlex] #{loc.path}:#{line}:#{loc.label}"
703
490
  else
704
- raise Phlex::ArgumentError.new("Invalid style value: #{v.inspect}")
491
+ "#{loc.path}:#{loc.lineno}:#{loc.label}"
705
492
  end
706
-
707
- if value
708
- if i == 0
709
- buffer << prop << ": " << value << ";"
710
- else
711
- buffer << " " << prop << ": " << value << ";"
712
- end
713
- end
714
-
715
- i += 1
716
493
  end
494
+ )
717
495
 
718
- buffer
719
- end
720
- end
721
-
722
- private_class_method def self.method_added(method_name)
723
- if method_name == :view_template
724
- location = instance_method(method_name).source_location[0]
725
-
726
- if location[0] in "/" | "."
727
- Phlex.__expand_attribute_cache__(location)
728
- end
729
- else
730
- super
731
- end
496
+ exception
732
497
  end
733
498
  end
data/lib/phlex/svg.rb CHANGED
@@ -44,11 +44,11 @@ class Phlex::SVG < Phlex::SGML
44
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
- buffer << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes)) << ">"
47
+ buffer << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes)) << ">"
48
48
  __yield_content__(&)
49
49
  buffer << "</#{tag}>"
50
50
  else # without content
51
- buffer << "<#{tag}" << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes)) << "></#{tag}>"
51
+ buffer << "<#{tag}" << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes)) << "></#{tag}>"
52
52
  end
53
53
  else # without attributes
54
54
  if block_given # with content block
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.3.1"
4
+ VERSION = "2.4.0.beta2"
5
5
  end
data/lib/phlex.rb CHANGED
@@ -27,6 +27,8 @@ module Phlex
27
27
  CACHED_FILES = Set.new
28
28
  ATTRIBUTE_CACHE = FIFO.new
29
29
 
30
+ UNBOUND_INSTANCE_METHOD_METHOD = Module.instance_method(:instance_method)
31
+
30
32
  def self.__expand_attribute_cache__(file_path)
31
33
  unless CACHED_FILES.include?(file_path)
32
34
  CACHED_FILES << file_path