phlex 1.10.3 → 2.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +21 -3
- data/lib/phlex/context.rb +0 -5
- data/lib/phlex/csv.rb +6 -12
- data/lib/phlex/element_clobbering_guard.rb +6 -2
- data/lib/phlex/elements.rb +52 -32
- data/lib/phlex/error.rb +4 -0
- data/lib/phlex/errors/argument_error.rb +5 -0
- data/lib/phlex/errors/double_render_error.rb +5 -0
- data/lib/phlex/errors/name_error.rb +5 -0
- data/lib/phlex/fifo.rb +47 -0
- data/lib/phlex/helpers.rb +27 -92
- data/lib/phlex/html/standard_elements.rb +1 -1
- data/lib/phlex/html/void_elements.rb +0 -6
- data/lib/phlex/html.rb +30 -49
- data/lib/phlex/kit.rb +28 -25
- data/lib/phlex/sgml/safe_object.rb +7 -0
- data/lib/phlex/sgml/safe_value.rb +11 -0
- data/lib/phlex/sgml.rb +524 -359
- data/lib/phlex/svg.rb +10 -12
- data/lib/phlex/testing/capybara.rb +28 -0
- data/lib/phlex/testing/nokogiri.rb +19 -0
- data/lib/phlex/testing/nokolexbor.rb +19 -0
- data/lib/phlex/testing/sgml.rb +9 -0
- data/lib/phlex/testing.rb +10 -0
- data/lib/phlex/version.rb +1 -1
- data/lib/phlex.rb +19 -35
- metadata +16 -7
- data/lib/phlex/callable.rb +0 -8
- data/lib/phlex/testing/view_helper.rb +0 -17
- data/lib/phlex/unbuffered.rb +0 -50
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fd808f50305105b7fb8cde4624804427da99fa7a3e59a20f6442e1db35e0983d
|
4
|
+
data.tar.gz: f3e7f7b703a27e3a00bbea42749e4682ac0255a3c3c12abca5c94f90de1be23c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d66595c47ec3704a3b06c619e2fe8697252a8e9f5566be7c0c8082c06c69a46361686135411f04268854a1a087a78b24e072e7ca4fa98fd23b64dfaa147f6f07
|
7
|
+
data.tar.gz: e93c80673a2ca16d9585d76b8c348ec0a7ddf04013720d083dc064b432aaf7f1642bbe5a6b8622fea6c18eb453f663de0d36929bd903f3b40a7f243987a46dee
|
data/README.md
CHANGED
@@ -1,10 +1,24 @@
|
|
1
1
|
<a href="https://www.phlex.fun/"><img alt="Phlex logo" src="https://www.phlex.fun/assets/logo.png" width="180" /></a>
|
2
2
|
|
3
|
-
Phlex lets you compose web views in pure Ruby
|
3
|
+
Phlex lets you compose web views in pure Ruby.
|
4
4
|
|
5
|
-
|
5
|
+
- [1.0 Stable Docs](https://www.phlex.fun)
|
6
|
+
- [2.0 Beta Docs](https://beta.phlex.fun)
|
6
7
|
|
7
|
-
|
8
|
+
## Maintenance schedule
|
9
|
+
|
10
|
+
### Bug fixes
|
11
|
+
|
12
|
+
- Only the latest minor version of each major version will receive bug fixes
|
13
|
+
- We may choose to fix bugs by releasing a new minor version rather than patching the existing minor version
|
14
|
+
- Major versions will stop receiving bug fixes one year after the next major version is released
|
15
|
+
|
16
|
+
### Security patches
|
17
|
+
|
18
|
+
- When a security issue is brought to our attention, we aim to release patches for any minor version that was released in the last year.
|
19
|
+
- Additionally, the latest minor version of the latest major version will receive security patches, even if that version is over a year old.
|
20
|
+
|
21
|
+
## Prior Art 🎨
|
8
22
|
|
9
23
|
- [markaby](https://github.com/markaby/markaby)
|
10
24
|
- [erector](https://github.com/erector/erector)
|
@@ -17,3 +31,7 @@ Docs and more at [Phlex.fun](https://www.phlex.fun/)
|
|
17
31
|
- [clearwater](https://github.com/clearwater-rb/clearwater)
|
18
32
|
- [paggio](https://github.com/opal/paggio)
|
19
33
|
- [Inesita](https://github.com/inesita-rb/inesita)
|
34
|
+
- [compony](https://github.com/kalsan/compony)
|
35
|
+
- [tagz](https://github.com/ahoward/tagz)
|
36
|
+
- [html](https://github.com/ismasan/html)
|
37
|
+
- [fortitude](https://github.com/ageweke/fortitude)
|
data/lib/phlex/context.rb
CHANGED
@@ -15,11 +15,6 @@ class Phlex::Context
|
|
15
15
|
|
16
16
|
attr_reader :fragments
|
17
17
|
|
18
|
-
# Added for backwards compatibility with phlex-rails. We can remove this with 2.0
|
19
|
-
def target
|
20
|
-
@buffer
|
21
|
-
end
|
22
|
-
|
23
18
|
def target_fragments(fragments)
|
24
19
|
@fragments = fragments.to_h { |it| [it, true] }
|
25
20
|
end
|
data/lib/phlex/csv.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Phlex::CSV
|
4
|
-
|
5
|
-
|
6
|
-
FORMULA_PREFIXES = ["=", "+", "-", "@", "\t", "\r"].to_h { |prefix| [prefix, true] }.freeze
|
7
|
-
SPACE_CHARACTERS = [" ", "\t", "\r"].to_h { |char| [char, true] }.freeze
|
4
|
+
FORMULA_PREFIXES = Set["=", "+", "-", "@", "\t", "\r"].freeze
|
5
|
+
SPACE_CHARACTERS = Set[" ", "\t", "\r"].freeze
|
8
6
|
|
9
7
|
def initialize(collection)
|
10
8
|
@collection = collection
|
@@ -84,18 +82,14 @@ class Phlex::CSV
|
|
84
82
|
@_current_column_index += 1
|
85
83
|
end
|
86
84
|
|
87
|
-
def each_item(&
|
88
|
-
collection.each(&
|
85
|
+
def each_item(&)
|
86
|
+
collection.each(&)
|
89
87
|
end
|
90
88
|
|
91
89
|
def yielder(record)
|
92
90
|
yield(record)
|
93
91
|
end
|
94
92
|
|
95
|
-
def template(...)
|
96
|
-
nil
|
97
|
-
end
|
98
|
-
|
99
93
|
# Override and set to `false` to disable rendering headers.
|
100
94
|
def render_headers?
|
101
95
|
true
|
@@ -120,11 +114,11 @@ class Phlex::CSV
|
|
120
114
|
first_char = value[0]
|
121
115
|
last_char = value[-1]
|
122
116
|
|
123
|
-
if escape_csv_injection? && FORMULA_PREFIXES
|
117
|
+
if escape_csv_injection? && FORMULA_PREFIXES.include?(first_char)
|
124
118
|
# Prefix a single quote to prevent Excel, Google Docs, etc. from interpreting the value as a formula.
|
125
119
|
# See https://owasp.org/www-community/attacks/CSV_Injection
|
126
120
|
%("'#{value.gsub('"', '""')}")
|
127
|
-
elsif (!trim_whitespace? && (SPACE_CHARACTERS
|
121
|
+
elsif (!trim_whitespace? && (SPACE_CHARACTERS.include?(first_char) || SPACE_CHARACTERS.include?(last_char))) || value.include?('"') || value.include?(",") || value.include?("\n")
|
128
122
|
%("#{value.gsub('"', '""')}")
|
129
123
|
else
|
130
124
|
value
|
@@ -3,8 +3,12 @@
|
|
3
3
|
# @api private
|
4
4
|
module Phlex::ElementClobberingGuard
|
5
5
|
def method_added(method_name)
|
6
|
-
if method_name[0] == "_" &&
|
7
|
-
raise Phlex::NameError
|
6
|
+
if method_name[0] == "_" && __element_method__?(method_name[1..].to_sym)
|
7
|
+
raise Phlex::NameError.new("👋 Redefining the method `#{name}##{method_name}` is not a good idea.")
|
8
|
+
elsif method_name == :view_template
|
9
|
+
Phlex.__expand_attribute_cache__(
|
10
|
+
instance_method(method_name).source_location[0],
|
11
|
+
)
|
8
12
|
else
|
9
13
|
super
|
10
14
|
end
|
data/lib/phlex/elements.rb
CHANGED
@@ -28,25 +28,16 @@ module Phlex::Elements
|
|
28
28
|
# @note The methods defined by this macro depend on other methods from {SGML} so they should always be mixed into an {HTML} or {SVG} component.
|
29
29
|
# @example Register the custom element `<trix-editor>`
|
30
30
|
# register_element :trix_editor
|
31
|
-
def register_element(method_name, tag: method_name.name.tr("_", "-")
|
32
|
-
if deprecated
|
33
|
-
deprecation = <<~RUBY
|
34
|
-
Kernel.warn "#{deprecated}"
|
35
|
-
RUBY
|
36
|
-
else
|
37
|
-
deprecation = ""
|
38
|
-
end
|
39
|
-
|
31
|
+
def register_element(method_name, tag: method_name.name.tr("_", "-"))
|
40
32
|
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
41
33
|
# frozen_string_literal: true
|
42
34
|
|
43
|
-
def #{method_name}(**attributes
|
44
|
-
#{deprecation}
|
45
|
-
|
35
|
+
def #{method_name}(**attributes)
|
46
36
|
context = @_context
|
47
37
|
buffer = context.buffer
|
48
38
|
fragment = context.fragments
|
49
39
|
target_found = false
|
40
|
+
block_given = block_given?
|
50
41
|
|
51
42
|
if fragment
|
52
43
|
return if fragment.length == 0 # we found all our fragments already
|
@@ -58,26 +49,64 @@ module Phlex::Elements
|
|
58
49
|
context.begin_target(id)
|
59
50
|
target_found = true
|
60
51
|
else
|
61
|
-
yield(self) if
|
52
|
+
yield(self) if block_given
|
62
53
|
return nil
|
63
54
|
end
|
64
55
|
end
|
65
56
|
end
|
66
57
|
|
67
58
|
if attributes.length > 0 # with attributes
|
68
|
-
if
|
69
|
-
buffer << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[
|
70
|
-
|
59
|
+
if block_given # with content block
|
60
|
+
buffer << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes)) << ">"
|
61
|
+
|
62
|
+
original_length = buffer.bytesize
|
63
|
+
content = yield(self)
|
64
|
+
if original_length == buffer.bytesize
|
65
|
+
case content
|
66
|
+
when String
|
67
|
+
buffer << Phlex::Escape.html_escape(content)
|
68
|
+
when Symbol
|
69
|
+
buffer << Phlex::Escape.html_escape(content.name)
|
70
|
+
when nil
|
71
|
+
nil
|
72
|
+
when Phlex::SGML::SafeObject
|
73
|
+
buffer << content.to_s
|
74
|
+
else
|
75
|
+
if (formatted_object = format_object(content))
|
76
|
+
buffer << Phlex::Escape.html_escape(formatted_object)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
71
81
|
buffer << "</#{tag}>"
|
72
|
-
else # without content
|
73
|
-
buffer << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[
|
82
|
+
else # without content
|
83
|
+
buffer << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes)) << "></#{tag}>"
|
74
84
|
end
|
75
85
|
else # without attributes
|
76
|
-
if
|
86
|
+
if block_given # with content block
|
77
87
|
buffer << "<#{tag}>"
|
78
|
-
|
88
|
+
|
89
|
+
original_length = buffer.bytesize
|
90
|
+
content = yield(self)
|
91
|
+
if original_length == buffer.bytesize
|
92
|
+
case content
|
93
|
+
when String
|
94
|
+
buffer << Phlex::Escape.html_escape(content)
|
95
|
+
when Symbol
|
96
|
+
buffer << Phlex::Escape.html_escape(content.name)
|
97
|
+
when nil
|
98
|
+
nil
|
99
|
+
when Phlex::SGML::SafeObject
|
100
|
+
buffer << content.to_s
|
101
|
+
else
|
102
|
+
if (formatted_object = format_object(content))
|
103
|
+
buffer << Phlex::Escape.html_escape(formatted_object)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
79
108
|
buffer << "</#{tag}>"
|
80
|
-
else # without content
|
109
|
+
else # without content
|
81
110
|
buffer << "<#{tag}></#{tag}>"
|
82
111
|
end
|
83
112
|
end
|
@@ -98,20 +127,11 @@ module Phlex::Elements
|
|
98
127
|
end
|
99
128
|
|
100
129
|
# @api private
|
101
|
-
def register_void_element(method_name, tag: method_name.name.tr("_", "-")
|
102
|
-
if deprecated
|
103
|
-
deprecation = <<~RUBY
|
104
|
-
Kernel.warn "#{deprecated}"
|
105
|
-
RUBY
|
106
|
-
else
|
107
|
-
deprecation = ""
|
108
|
-
end
|
109
|
-
|
130
|
+
def register_void_element(method_name, tag: method_name.name.tr("_", "-"))
|
110
131
|
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
111
132
|
# frozen_string_literal: true
|
112
133
|
|
113
134
|
def #{method_name}(**attributes)
|
114
|
-
#{deprecation}
|
115
135
|
context = @_context
|
116
136
|
buffer = context.buffer
|
117
137
|
fragment = context.fragments
|
@@ -132,7 +152,7 @@ module Phlex::Elements
|
|
132
152
|
end
|
133
153
|
|
134
154
|
if attributes.length > 0 # with attributes
|
135
|
-
buffer << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[
|
155
|
+
buffer << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes)) << ">"
|
136
156
|
else # without attributes
|
137
157
|
buffer << "<#{tag}>"
|
138
158
|
end
|
data/lib/phlex/error.rb
ADDED
data/lib/phlex/fifo.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Phlex::FIFO
|
4
|
+
def initialize(max_bytesize: 2_000, max_value_bytesize: 2_000)
|
5
|
+
@store = {}
|
6
|
+
@max_bytesize = max_bytesize
|
7
|
+
@max_value_bytesize = max_value_bytesize
|
8
|
+
@bytesize = 0
|
9
|
+
@mutex = Mutex.new
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :bytesize, :max_bytesize
|
13
|
+
|
14
|
+
def expand(bytes)
|
15
|
+
@mutex.synchronize do
|
16
|
+
@max_bytesize += bytes
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def [](key)
|
21
|
+
k, v = @store[key.hash]
|
22
|
+
v if k == key
|
23
|
+
end
|
24
|
+
|
25
|
+
def []=(key, value)
|
26
|
+
return if value.bytesize > @max_value_bytesize
|
27
|
+
|
28
|
+
digest = key.hash
|
29
|
+
|
30
|
+
@mutex.synchronize do
|
31
|
+
# Check the key definitely doesn't exist now we have the lock
|
32
|
+
return if @store[digest]
|
33
|
+
|
34
|
+
@store[digest] = [key, value]
|
35
|
+
@bytesize += value.bytesize
|
36
|
+
|
37
|
+
while @bytesize > @max_bytesize
|
38
|
+
k, v = @store.shift
|
39
|
+
@bytesize -= v[1].bytesize
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def size
|
45
|
+
@store.size
|
46
|
+
end
|
47
|
+
end
|
data/lib/phlex/helpers.rb
CHANGED
@@ -1,104 +1,31 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "set"
|
4
|
-
|
5
3
|
module Phlex::Helpers
|
6
4
|
private
|
7
5
|
|
8
|
-
# Tokens
|
9
|
-
# @return [String]
|
10
|
-
# @example With Proc conditions
|
11
|
-
# tokens(
|
12
|
-
# -> { true } => "a",
|
13
|
-
# -> { false } => "b"
|
14
|
-
# )
|
15
|
-
# @example With method conditions
|
16
|
-
# tokens(
|
17
|
-
# active?: "active"
|
18
|
-
# )
|
19
|
-
# @example With else condition
|
20
|
-
# tokens(
|
21
|
-
# active?: { then: "active", else: "inactive" }
|
22
|
-
# )
|
23
|
-
def tokens(*tokens, **conditional_tokens)
|
24
|
-
conditional_tokens.each do |condition, token|
|
25
|
-
truthy = case condition
|
26
|
-
when Symbol then send(condition)
|
27
|
-
when Proc then condition.call
|
28
|
-
else raise ArgumentError, "The class condition must be a Symbol or a Proc."
|
29
|
-
end
|
30
|
-
|
31
|
-
if truthy
|
32
|
-
case token
|
33
|
-
when Hash then __append_token__(tokens, token[:then])
|
34
|
-
else __append_token__(tokens, token)
|
35
|
-
end
|
36
|
-
else
|
37
|
-
case token
|
38
|
-
when Hash then __append_token__(tokens, token[:else])
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
tokens = tokens.select(&:itself).join(" ")
|
44
|
-
tokens.strip!
|
45
|
-
tokens.gsub!(/\s+/, " ")
|
46
|
-
tokens
|
47
|
-
end
|
48
|
-
|
49
|
-
# @api private
|
50
|
-
def __append_token__(tokens, token)
|
51
|
-
case token
|
52
|
-
when nil then nil
|
53
|
-
when String then tokens << token
|
54
|
-
when Symbol then tokens << token.name
|
55
|
-
when Array then tokens.concat(token)
|
56
|
-
else raise ArgumentError,
|
57
|
-
"Conditional classes must be Symbols, Strings, or Arrays of Symbols or Strings."
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
# Like {#tokens} but returns a {Hash} where the tokens are the value for `:class`.
|
62
|
-
# @return [Hash]
|
63
|
-
def classes(*tokens, **conditional_tokens)
|
64
|
-
tokens = self.tokens(*tokens, **conditional_tokens)
|
65
|
-
|
66
|
-
if tokens.empty?
|
67
|
-
{}
|
68
|
-
else
|
69
|
-
{ class: tokens }
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
6
|
# @return [Hash]
|
74
7
|
def mix(*args)
|
75
8
|
args.each_with_object({}) do |object, result|
|
76
9
|
result.merge!(object) do |_key, old, new|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
old
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
new + [old]
|
97
|
-
end
|
98
|
-
when String
|
99
|
-
old.is_a?(String) ? "#{old} #{new}" : old + old.class[new]
|
100
|
-
when nil
|
101
|
-
old
|
10
|
+
case [old, new]
|
11
|
+
in [Array, Array] | [Set, Set]
|
12
|
+
old + new
|
13
|
+
in [Array, Set]
|
14
|
+
old + new.to_a
|
15
|
+
in [Array, String]
|
16
|
+
old + [new]
|
17
|
+
in [Hash, Hash]
|
18
|
+
mix(old, new)
|
19
|
+
in [Set, Array]
|
20
|
+
old.to_a + new
|
21
|
+
in [Set, String]
|
22
|
+
old.to_a + [new]
|
23
|
+
in [String, Array]
|
24
|
+
[old] + new
|
25
|
+
in [String, Set]
|
26
|
+
[old] + new.to_a
|
27
|
+
in [String, String]
|
28
|
+
"#{old} #{new}"
|
102
29
|
else
|
103
30
|
new
|
104
31
|
end
|
@@ -109,4 +36,12 @@ module Phlex::Helpers
|
|
109
36
|
end
|
110
37
|
end
|
111
38
|
end
|
39
|
+
|
40
|
+
def grab(**bindings)
|
41
|
+
if bindings.size > 1
|
42
|
+
bindings.values
|
43
|
+
else
|
44
|
+
bindings.values.first
|
45
|
+
end
|
46
|
+
end
|
112
47
|
end
|
@@ -639,7 +639,7 @@ module Phlex::HTML::StandardElements
|
|
639
639
|
# @return [nil]
|
640
640
|
# @yieldparam component [self]
|
641
641
|
# @see https://developer.mozilla.org/docs/Web/HTML/Element/template
|
642
|
-
register_element :
|
642
|
+
register_element :template
|
643
643
|
|
644
644
|
# @!method textarea(**attributes, &content)
|
645
645
|
# Outputs a `<textarea>` tag.
|
@@ -58,12 +58,6 @@ module Phlex::HTML::VoidElements
|
|
58
58
|
# @see https://developer.mozilla.org/docs/Web/HTML/Element/meta
|
59
59
|
register_void_element :meta
|
60
60
|
|
61
|
-
# @!method param(**attributes, &content)
|
62
|
-
# Outputs a `<param>` tag.
|
63
|
-
# @return [nil]
|
64
|
-
# @see https://developer.mozilla.org/docs/Web/HTML/Element/param
|
65
|
-
register_void_element :param, deprecated: "⚠️ [DEPRECATION] The <param> tag is deprecated. See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/param"
|
66
|
-
|
67
61
|
# @!method source(**attributes, &content)
|
68
62
|
# Outputs a `<source>` tag.
|
69
63
|
# @return [nil]
|
data/lib/phlex/html.rb
CHANGED
@@ -1,66 +1,47 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
autoload :StandardElements, "phlex/html/standard_elements"
|
7
|
-
autoload :VoidElements, "phlex/html/void_elements"
|
3
|
+
class Phlex::HTML < Phlex::SGML
|
4
|
+
autoload :StandardElements, "phlex/html/standard_elements"
|
5
|
+
autoload :VoidElements, "phlex/html/void_elements"
|
8
6
|
|
9
|
-
|
10
|
-
|
7
|
+
# A list of HTML attributes that have the potential to execute unsafe JavaScript.
|
8
|
+
UNSAFE_ATTRIBUTES = Set.new(%w[onabort onafterprint onbeforeprint onbeforeunload onblur oncanplay oncanplaythrough onchange onclick oncontextmenu oncopy oncuechange oncut ondblclick ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended onerror onfocus onhashchange oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart onmessage onmousedown onmousemove onmouseout onmouseover onmouseup onmousewheel onoffline ononline onpagehide onpageshow onpaste onpause onplay onplaying onpopstate onprogress onratechange onreset onresize onscroll onsearch onseeked onseeking onselect onstalled onstorage onsubmit onsuspend ontimeupdate ontoggle onunload onvolumechange onwaiting onwheel srcdoc]).freeze
|
11
9
|
|
12
|
-
|
10
|
+
extend Phlex::Elements
|
11
|
+
include VoidElements, StandardElements
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
if defined? @unbuffered_class
|
19
|
-
@unbuffered_class
|
20
|
-
else
|
21
|
-
@unbuffered_class = Class.new(Unbuffered)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
extend Elements
|
28
|
-
include VoidElements, StandardElements
|
13
|
+
# Output an HTML doctype.
|
14
|
+
def doctype
|
15
|
+
context = @_context
|
16
|
+
return if context.fragments && !context.in_target_fragment
|
29
17
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
return if context.fragments && !context.in_target_fragment
|
34
|
-
|
35
|
-
context.buffer << "<!DOCTYPE html>"
|
36
|
-
nil
|
37
|
-
end
|
18
|
+
context.buffer << "<!doctype html>"
|
19
|
+
nil
|
20
|
+
end
|
38
21
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
22
|
+
# Outputs an `<svg>` tag
|
23
|
+
# @return [nil]
|
24
|
+
# @see https://developer.mozilla.org/docs/Web/SVG/Element/svg
|
25
|
+
def svg(...)
|
26
|
+
if block_given?
|
43
27
|
super do
|
44
28
|
render Phlex::SVG.new do |svg|
|
45
29
|
yield(svg)
|
46
30
|
end
|
47
31
|
end
|
32
|
+
else
|
33
|
+
super
|
48
34
|
end
|
35
|
+
end
|
49
36
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
end
|
54
|
-
|
55
|
-
def filename
|
56
|
-
nil
|
57
|
-
end
|
58
|
-
|
59
|
-
def content_type
|
60
|
-
"text/html"
|
61
|
-
end
|
37
|
+
def filename
|
38
|
+
nil
|
39
|
+
end
|
62
40
|
|
63
|
-
|
64
|
-
|
41
|
+
def content_type
|
42
|
+
"text/html"
|
65
43
|
end
|
44
|
+
|
45
|
+
# This should be extended after all method definitions
|
46
|
+
extend Phlex::ElementClobberingGuard
|
66
47
|
end
|
data/lib/phlex/kit.rb
CHANGED
@@ -1,48 +1,51 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Phlex::Kit
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
module LazyLoader
|
5
|
+
def method_missing(name, ...)
|
6
|
+
if name[0] == name[0].upcase && __phlex_kit_constants__.include?(name) && __get_phlex_kit_constant__(name) && methods.include?(name)
|
7
|
+
public_send(name, ...)
|
8
|
+
else
|
9
|
+
super
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def respond_to_missing?(name, include_private = false)
|
14
|
+
if name[0] == name[0].upcase && __phlex_kit_constants__.include?(name) && __get_phlex_kit_constant__(name) && methods.include?(name)
|
15
|
+
true
|
16
|
+
else
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
7
20
|
end
|
8
21
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
22
|
+
include LazyLoader
|
23
|
+
|
24
|
+
def self.extended(mod)
|
25
|
+
mod.include(LazyLoader)
|
26
|
+
mod.define_method(:__phlex_kit_constants__) { mod.__phlex_kit_constants__ }
|
27
|
+
mod.define_method(:__get_phlex_kit_constant__) { |name| mod.__get_phlex_kit_constant__(name) }
|
13
28
|
end
|
14
29
|
|
15
|
-
def
|
16
|
-
|
17
|
-
public_send(name, *args, **kwargs, &block)
|
18
|
-
else
|
19
|
-
super
|
20
|
-
end
|
30
|
+
def __phlex_kit_constants__
|
31
|
+
constants
|
21
32
|
end
|
22
33
|
|
23
|
-
def
|
24
|
-
|
25
|
-
true
|
26
|
-
else
|
27
|
-
super
|
28
|
-
end
|
34
|
+
def __get_phlex_kit_constant__(name)
|
35
|
+
const_get(name)
|
29
36
|
end
|
30
37
|
|
31
38
|
def const_added(name)
|
32
39
|
return if autoload?(name)
|
33
40
|
|
41
|
+
me = self
|
34
42
|
constant = const_get(name)
|
35
43
|
|
36
44
|
if Class === constant && constant < Phlex::SGML
|
37
|
-
if instance_methods.include?(name)
|
38
|
-
raise NameError, "The instance method `#{name}' is already defined on `#{inspect}`."
|
39
|
-
elsif methods.include?(name)
|
40
|
-
raise NameError, "The method `#{name}' is already defined on `#{inspect}`."
|
41
|
-
end
|
42
|
-
|
43
45
|
constant.include(self)
|
44
46
|
|
45
47
|
define_method(name) do |*args, **kwargs, &block|
|
48
|
+
constant = me.const_get(name)
|
46
49
|
render(constant.new(*args, **kwargs), &block)
|
47
50
|
end
|
48
51
|
|