rbexy 1.0.2 → 2.0.0.beta4
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/Gemfile.lock +3 -1
- data/README.md +18 -0
- data/lib/rbexy.rb +27 -9
- data/lib/rbexy/ast_transformer.rb +21 -0
- data/lib/rbexy/component.rb +10 -18
- data/lib/rbexy/component_context.rb +18 -0
- data/lib/rbexy/component_resolver.rb +60 -0
- data/lib/rbexy/configuration.rb +18 -1
- data/lib/rbexy/lexer.rb +21 -14
- data/lib/rbexy/nodes.rb +15 -135
- data/lib/rbexy/nodes/abstract_attr.rb +12 -0
- data/lib/rbexy/nodes/abstract_element.rb +13 -0
- data/lib/rbexy/nodes/abstract_node.rb +58 -0
- data/lib/rbexy/nodes/component_element.rb +69 -0
- data/lib/rbexy/nodes/component_prop.rb +29 -0
- data/lib/rbexy/nodes/declaration.rb +15 -0
- data/lib/rbexy/nodes/expression.rb +15 -0
- data/lib/rbexy/nodes/expression_group.rb +55 -0
- data/lib/rbexy/nodes/html_attr.rb +13 -0
- data/lib/rbexy/nodes/html_element.rb +48 -0
- data/lib/rbexy/nodes/newline.rb +9 -0
- data/lib/rbexy/nodes/raw.rb +23 -0
- data/lib/rbexy/nodes/root.rb +19 -0
- data/lib/rbexy/nodes/text.rb +15 -0
- data/lib/rbexy/nodes/util.rb +9 -0
- data/lib/rbexy/parser.rb +22 -16
- data/lib/rbexy/rails/component_template_resolver.rb +3 -3
- data/lib/rbexy/rails/controller_helper.rb +5 -4
- data/lib/rbexy/rails/engine.rb +1 -11
- data/lib/rbexy/refinements.rb +5 -0
- data/lib/rbexy/refinements/array.rb +9 -0
- data/lib/rbexy/refinements/array/find_map.rb +13 -0
- data/lib/rbexy/refinements/array/insert_between_types.rb +26 -0
- data/lib/rbexy/refinements/array/map_type_when_neighboring_type.rb +26 -0
- data/lib/rbexy/runtime.rb +16 -23
- data/lib/rbexy/template.rb +12 -0
- data/lib/rbexy/version.rb +1 -1
- data/rbexy.gemspec +1 -0
- metadata +42 -11
- data/example.rb +0 -113
- data/lib/rbexy/component_providers/namespaced_rbexy_provider.rb +0 -20
- data/lib/rbexy/component_providers/rbexy_provider.rb +0 -21
- data/lib/rbexy/component_providers/view_component_provider.rb +0 -21
- data/lib/rbexy/component_tag_builder.rb +0 -19
- data/lib/rbexy/hash_mash.rb +0 -15
- data/lib/rbexy/view_context_helper.rb +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d9cb20a821099f8fd51a0abb4b80ad3b2d8d936afcfe240a4b99ea29f4dea92a
|
4
|
+
data.tar.gz: aab32903c9632f245857c917b0b99743e5e58ddb8356fce1274c5dd2e4791b37
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a849303b1f41e2e6295558b09f1a608a3b24bca8317bde650c579c6a0da2206c67f1ea9bb49e4b7bf15e66497960fff70703845544a57271c4947e4abe16d5ef
|
7
|
+
data.tar.gz: 5f08d1838d39cf38d3f3d727e5c1ea098255e81e4a2ab4affd2b67a77a10d7f55dccdff52114f5ce9d544238d81d09a66f548ed74961298026f7887ad30339fd
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rbexy (
|
4
|
+
rbexy (2.0.0.beta4)
|
5
5
|
actionview (>= 6.0, < 7.0)
|
6
6
|
activesupport (>= 6.0, < 7.0)
|
7
7
|
|
@@ -101,6 +101,7 @@ GEM
|
|
101
101
|
mini_mime (>= 0.1.1)
|
102
102
|
marcel (0.3.3)
|
103
103
|
mimemagic (~> 0.3.2)
|
104
|
+
memory_profiler (0.9.14)
|
104
105
|
method_source (1.0.0)
|
105
106
|
mimemagic (0.3.5)
|
106
107
|
mini_mime (1.0.2)
|
@@ -199,6 +200,7 @@ PLATFORMS
|
|
199
200
|
|
200
201
|
DEPENDENCIES
|
201
202
|
guard-rspec (~> 4.7, >= 4.7.3)
|
203
|
+
memory_profiler (~> 0.9.14)
|
202
204
|
pry-byebug
|
203
205
|
rails (>= 6.0, < 7.0)
|
204
206
|
rake
|
data/README.md
CHANGED
@@ -173,6 +173,14 @@ Loops:
|
|
173
173
|
</ul>
|
174
174
|
```
|
175
175
|
|
176
|
+
Blocks:
|
177
|
+
|
178
|
+
```jsx
|
179
|
+
{link_to "/" do
|
180
|
+
<span>Click me</span>
|
181
|
+
end}
|
182
|
+
```
|
183
|
+
|
176
184
|
As an attribute:
|
177
185
|
|
178
186
|
```jsx
|
@@ -189,6 +197,16 @@ Pass a lambda to a prop, that when called returns a tag:
|
|
189
197
|
</Hero>
|
190
198
|
```
|
191
199
|
|
200
|
+
_Note that when using tags inside blocks, the block must evaluate to a single root element. Rbexy behaves similar to JSX in this way. E.g.:_
|
201
|
+
|
202
|
+
```
|
203
|
+
# Do
|
204
|
+
-> { <span><i>Hello</i> World</span> }
|
205
|
+
|
206
|
+
# Don't
|
207
|
+
-> { <i>Hello</i> World }
|
208
|
+
```
|
209
|
+
|
192
210
|
### Tags
|
193
211
|
|
194
212
|
You can put standard HTML tags anywhere.
|
data/lib/rbexy.rb
CHANGED
@@ -1,14 +1,22 @@
|
|
1
1
|
require "rbexy/version"
|
2
|
+
require "active_support/inflector"
|
3
|
+
require "active_support/concern"
|
4
|
+
require "action_view/helpers/output_safety_helper"
|
5
|
+
require "action_view/helpers/capture_helper"
|
6
|
+
require "action_view/helpers/tag_helper"
|
7
|
+
require "action_view/context"
|
2
8
|
|
3
9
|
module Rbexy
|
4
10
|
autoload :Lexer, "rbexy/lexer"
|
5
11
|
autoload :Parser, "rbexy/parser"
|
6
12
|
autoload :Nodes, "rbexy/nodes"
|
7
13
|
autoload :Runtime, "rbexy/runtime"
|
8
|
-
autoload :
|
9
|
-
autoload :ComponentTagBuilder, "rbexy/component_tag_builder"
|
10
|
-
autoload :ViewContextHelper, "rbexy/view_context_helper"
|
14
|
+
autoload :ComponentContext, "rbexy/component_context"
|
11
15
|
autoload :Configuration, "rbexy/configuration"
|
16
|
+
autoload :ComponentResolver, "rbexy/component_resolver"
|
17
|
+
autoload :Template, "rbexy/template"
|
18
|
+
autoload :Refinements, "rbexy/refinements"
|
19
|
+
autoload :ASTTransformer, "rbexy/ast_transformer"
|
12
20
|
|
13
21
|
ContextNotFound = Class.new(StandardError)
|
14
22
|
|
@@ -21,14 +29,24 @@ module Rbexy
|
|
21
29
|
@configuration ||= Configuration.new
|
22
30
|
end
|
23
31
|
|
24
|
-
def compile(
|
25
|
-
tokens =
|
26
|
-
|
27
|
-
|
32
|
+
def compile(template, context = build_default_compile_context(template))
|
33
|
+
tokens = Lexer.new(template, context.element_resolver).tokenize
|
34
|
+
root = Parser.new(tokens).parse
|
35
|
+
root.inject_compile_context(context)
|
36
|
+
root.transform!
|
37
|
+
root.precompile.compile
|
28
38
|
end
|
29
39
|
|
30
|
-
def evaluate(template_string, runtime)
|
31
|
-
runtime.evaluate compile(template_string)
|
40
|
+
def evaluate(template_string, runtime = Rbexy::Runtime.new)
|
41
|
+
runtime.evaluate compile(Template.new(template_string))
|
42
|
+
end
|
43
|
+
|
44
|
+
def build_default_compile_context(template)
|
45
|
+
OpenStruct.new(
|
46
|
+
template: template,
|
47
|
+
element_resolver: configuration.element_resolver,
|
48
|
+
ast_transformer: configuration.transforms
|
49
|
+
)
|
32
50
|
end
|
33
51
|
end
|
34
52
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Rbexy
|
2
|
+
class ASTTransformer
|
3
|
+
attr_reader :registry
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
clear!
|
7
|
+
end
|
8
|
+
|
9
|
+
def register(*node_classes, &block)
|
10
|
+
node_classes.each { |k| (registry[k] ||= []) << block }
|
11
|
+
end
|
12
|
+
|
13
|
+
def transform(node, context)
|
14
|
+
registry[node.class]&.each { |t| t.call(node, context) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def clear!
|
18
|
+
@registry = {}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/rbexy/component.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require "action_view"
|
2
|
-
require "active_support/core_ext/class/attribute"
|
3
2
|
|
4
3
|
module Rbexy
|
5
4
|
class Component < ActionView::Base
|
@@ -28,7 +27,7 @@ module Rbexy
|
|
28
27
|
|
29
28
|
@view_context = view_context
|
30
29
|
|
31
|
-
|
30
|
+
after_initialize(**props)
|
32
31
|
end
|
33
32
|
|
34
33
|
# Override in your subclass to handle props, setup your component, etc.
|
@@ -54,17 +53,6 @@ module Rbexy
|
|
54
53
|
content_block ? content_block.call : ""
|
55
54
|
end
|
56
55
|
|
57
|
-
def create_context(name, value)
|
58
|
-
rbexy_context.last[name] = value
|
59
|
-
end
|
60
|
-
|
61
|
-
def use_context(name)
|
62
|
-
index = rbexy_context.rindex { |c| c.has_key?(name) }
|
63
|
-
index ?
|
64
|
-
rbexy_context[index][name] :
|
65
|
-
raise(ContextNotFound, "no parent context `#{name}`")
|
66
|
-
end
|
67
|
-
|
68
56
|
def compiled_method_container
|
69
57
|
Rbexy::Component
|
70
58
|
end
|
@@ -74,16 +62,20 @@ module Rbexy
|
|
74
62
|
attr_reader :view_context, :content_block
|
75
63
|
|
76
64
|
def method_missing(meth, *args, &block)
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
65
|
+
view_context.send(meth, *args, &block)
|
66
|
+
end
|
67
|
+
|
68
|
+
def respond_to_missing?(method_name, include_all)
|
69
|
+
view_context.respond_to?(method_name, include_all)
|
82
70
|
end
|
83
71
|
|
84
72
|
def clean_template_backtrace(backtrace)
|
85
73
|
return backtrace if Rbexy.configuration.debug
|
86
74
|
BacktraceCleaner.new(backtrace).call
|
87
75
|
end
|
76
|
+
|
77
|
+
def after_initialize(**props)
|
78
|
+
setup(**props)
|
79
|
+
end
|
88
80
|
end
|
89
81
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Rbexy
|
2
|
+
module ComponentContext
|
3
|
+
def rbexy_context
|
4
|
+
@rbexy_context ||= [{}]
|
5
|
+
end
|
6
|
+
|
7
|
+
def create_context(name, value)
|
8
|
+
rbexy_context.last[name] = value
|
9
|
+
end
|
10
|
+
|
11
|
+
def use_context(name)
|
12
|
+
index = rbexy_context.rindex { |c| c.has_key?(name) }
|
13
|
+
index ?
|
14
|
+
rbexy_context[index][name] :
|
15
|
+
raise(ContextNotFound, "no parent context `#{name}`")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Rbexy
|
2
|
+
class ComponentResolver
|
3
|
+
using Rbexy::Refinements::Array::FindMap
|
4
|
+
|
5
|
+
KNOWN_HTML_ELEMENTS = %w(
|
6
|
+
a abbr acronym address animate animateMotion animateTransform applet area article aside audio b base basefont
|
7
|
+
bdi bdo bgsound big blink blockquote body br button canvas caption center circle cite clipPath code col colgroup
|
8
|
+
color-profile command content data datalist dd defs del desc details dfn dialog dir discard div dl dt element
|
9
|
+
ellipse em embed feBlend feColorMatrix feComponentTransfer feComposite feConvolveMatrix feDiffuseLighting
|
10
|
+
feDisplacementMap feDistantLight feDropShadow feFlood feFuncA feFuncB feFuncG feFuncR feGaussianBlur feImage
|
11
|
+
feMerge feMergeNode feMorphology feOffset fePointLight feSpecularLighting feSpotLight feTile feTurbulence
|
12
|
+
fieldset figcaption figure filter font footer foreignObject form frame frameset g h1 h2 h3 h4 h5 h6 hatch
|
13
|
+
hatchpath head header hgroup hr html i iframe image img input ins isindex kbd keygen label legend li line
|
14
|
+
linearGradient link listing main map mark marker marquee mask menu menuitem mesh meshgradient meshpatch meshrow
|
15
|
+
meta metadata meter mpath multicol nav nextid nobr noembed noframes noscript object ol optgroup option output p
|
16
|
+
param path pattern picture plaintext polygon polyline pre progress q radialGradient rb rect rp rt rtc ruby s
|
17
|
+
samp script section select set shadow slot small solidcolor source spacer span stop strike strong style sub
|
18
|
+
summary sup svg switch symbol table tbody td template text textarea textPath tfoot th thead time title tr track
|
19
|
+
tspan tt u ul unknown use var video view wbr xmp
|
20
|
+
).to_set
|
21
|
+
|
22
|
+
attr_reader :component_namespaces
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
self.component_namespaces = {}
|
26
|
+
end
|
27
|
+
|
28
|
+
def component_namespaces=(hash)
|
29
|
+
@component_namespaces = hash.transform_keys(&:to_s)
|
30
|
+
end
|
31
|
+
|
32
|
+
def component?(name, template)
|
33
|
+
return false if KNOWN_HTML_ELEMENTS.include?(name)
|
34
|
+
return true if component_class(name, template)
|
35
|
+
false
|
36
|
+
end
|
37
|
+
|
38
|
+
def component_class(name, template)
|
39
|
+
possible_names = matching_namespaces(template).map { |ns| "#{ns}.#{name}" } << name
|
40
|
+
possible_names.find_map(&method(:find))
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def find(name)
|
46
|
+
find!(name)
|
47
|
+
rescue NameError => e
|
48
|
+
raise e unless e.message =~ /wrong constant name/ || e.message =~ /uninitialized constant/
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def matching_namespaces(template)
|
53
|
+
component_namespaces.select { |path, ns| template.identifier.start_with?(path) }.values.flatten.uniq
|
54
|
+
end
|
55
|
+
|
56
|
+
def find!(name)
|
57
|
+
ActiveSupport::Inflector.constantize("#{name.gsub(".", "::")}Component")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/rbexy/configuration.rb
CHANGED
@@ -1,12 +1,29 @@
|
|
1
1
|
module Rbexy
|
2
2
|
class Configuration
|
3
|
-
attr_accessor :
|
3
|
+
attr_accessor :element_resolver
|
4
4
|
attr_accessor :template_paths
|
5
5
|
attr_accessor :enable_context
|
6
6
|
attr_accessor :debug
|
7
|
+
attr_accessor :component_rendering_templates
|
8
|
+
attr_accessor :transforms
|
7
9
|
|
8
10
|
def template_paths
|
9
11
|
@template_paths ||= []
|
10
12
|
end
|
13
|
+
|
14
|
+
def element_resolver
|
15
|
+
@element_resolver ||= ComponentResolver.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def transforms
|
19
|
+
@transforms ||= ASTTransformer.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def component_rendering_templates
|
23
|
+
@component_rendering_templates ||= {
|
24
|
+
children: "{capture{%{children}}}",
|
25
|
+
component: "::%{component_class}.new(%{view_context},%{kwargs}).render%{children_block}"
|
26
|
+
}
|
27
|
+
end
|
11
28
|
end
|
12
29
|
end
|
data/lib/rbexy/lexer.rb
CHANGED
@@ -10,7 +10,7 @@ module Rbexy
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
Patterns =
|
13
|
+
Patterns = OpenStruct.new(
|
14
14
|
open_expression: /{/,
|
15
15
|
close_expression: /}/,
|
16
16
|
expression_content: /[^}{"'<]+/,
|
@@ -29,23 +29,23 @@ module Rbexy
|
|
29
29
|
single_quote: /'/,
|
30
30
|
double_quoted_text_content: /[^"]+/,
|
31
31
|
single_quoted_text_content: /[^']+/,
|
32
|
-
expression_internal_tag_prefixes: /(\s+(
|
32
|
+
expression_internal_tag_prefixes: /(\s+(&&|\|\||\?|:|do|do\s*\|[^\|]+\||{|{\s*\|[^\|]+\|)\s+\z|\A\s*\z)/,
|
33
33
|
declaration: /<![^>]*>/
|
34
34
|
)
|
35
35
|
|
36
|
-
attr_reader :stack, :tokens, :scanner, :
|
37
|
-
attr_accessor :
|
36
|
+
attr_reader :stack, :tokens, :scanner, :element_resolver, :template
|
37
|
+
attr_accessor :curr_expr, :curr_default_text,
|
38
38
|
:curr_quoted_text
|
39
39
|
|
40
|
-
def initialize(
|
40
|
+
def initialize(template, element_resolver)
|
41
|
+
@template = template
|
42
|
+
@scanner = StringScanner.new(template.source)
|
43
|
+
@element_resolver = element_resolver
|
41
44
|
@stack = [:default]
|
42
|
-
@curr_expr_bracket_levels = 0
|
43
|
-
@curr_expr_quote_levels = { single: 0, double: 0 }
|
44
45
|
@curr_expr = ""
|
45
46
|
@curr_default_text = ""
|
46
47
|
@curr_quoted_text = ""
|
47
48
|
@tokens = []
|
48
|
-
@scanner = StringScanner.new(code)
|
49
49
|
end
|
50
50
|
|
51
51
|
def tokenize
|
@@ -59,7 +59,7 @@ module Rbexy
|
|
59
59
|
elsif scanner.scan(Patterns.open_expression)
|
60
60
|
open_expression
|
61
61
|
elsif scanner.scan(Patterns.comment)
|
62
|
-
tokens << [:
|
62
|
+
tokens << [:NEWLINE]
|
63
63
|
elsif scanner.check(Patterns.text_content)
|
64
64
|
stack.push(:default_text)
|
65
65
|
else
|
@@ -74,7 +74,7 @@ module Rbexy
|
|
74
74
|
elsif scanner.scan(Patterns.open_expression)
|
75
75
|
open_expression
|
76
76
|
elsif scanner.scan(Patterns.comment)
|
77
|
-
tokens << [:
|
77
|
+
tokens << [:NEWLINE]
|
78
78
|
elsif scanner.check(Patterns.text_content)
|
79
79
|
stack.push(:default_text)
|
80
80
|
else
|
@@ -163,9 +163,9 @@ module Rbexy
|
|
163
163
|
tokens << [:CLOSE_TAG_DEF]
|
164
164
|
stack.pop
|
165
165
|
elsif scanner.scan(Patterns.tag_name)
|
166
|
-
tokens << [:
|
166
|
+
tokens << [:TAG_DETAILS, tag_details(scanner.matched)]
|
167
167
|
elsif scanner.scan(Patterns.whitespace)
|
168
|
-
scanner.matched.count("\n").times { tokens << [:
|
168
|
+
scanner.matched.count("\n").times { tokens << [:NEWLINE] }
|
169
169
|
tokens << [:OPEN_ATTRS]
|
170
170
|
stack.push(:tag_attrs)
|
171
171
|
else
|
@@ -182,7 +182,7 @@ module Rbexy
|
|
182
182
|
end
|
183
183
|
when :tag_attrs
|
184
184
|
if scanner.scan(Patterns.whitespace)
|
185
|
-
scanner.matched.count("\n").times { tokens << [:
|
185
|
+
scanner.matched.count("\n").times { tokens << [:NEWLINE] }
|
186
186
|
elsif scanner.check(Patterns.close_tag)
|
187
187
|
tokens << [:CLOSE_ATTRS]
|
188
188
|
stack.pop
|
@@ -205,7 +205,7 @@ module Rbexy
|
|
205
205
|
open_expression
|
206
206
|
elsif scanner.scan(Patterns.whitespace) || scanner.check(Patterns.close_tag)
|
207
207
|
tokens << [:CLOSE_ATTR_VALUE]
|
208
|
-
scanner.matched.count("\n").times { tokens << [:
|
208
|
+
scanner.matched.count("\n").times { tokens << [:NEWLINE] }
|
209
209
|
stack.pop
|
210
210
|
else
|
211
211
|
raise SyntaxError, self
|
@@ -286,5 +286,12 @@ module Rbexy
|
|
286
286
|
# etc).
|
287
287
|
scanner.scan(Patterns.expression_content) || scanner.scan(Patterns.open_tag_end)
|
288
288
|
end
|
289
|
+
|
290
|
+
def tag_details(name)
|
291
|
+
type = element_resolver.component?(name, template) ? :component : :html
|
292
|
+
details = { name: scanner.matched, type: type }
|
293
|
+
details[:component_class] = element_resolver.component_class(name, template) if type == :component
|
294
|
+
details
|
295
|
+
end
|
289
296
|
end
|
290
297
|
end
|
data/lib/rbexy/nodes.rb
CHANGED
@@ -1,139 +1,19 @@
|
|
1
1
|
module Rbexy
|
2
2
|
module Nodes
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
19
|
-
|
20
|
-
def compile
|
21
|
-
"#{children.map(&:compile).map { |c| "@output_buffer << rbexy_prep_output(#{c})"}.join(";")};@output_buffer"
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class Text
|
26
|
-
attr_reader :content
|
27
|
-
|
28
|
-
def initialize(content)
|
29
|
-
@content = content
|
30
|
-
end
|
31
|
-
|
32
|
-
def compile
|
33
|
-
"\"#{Util.safe_string(content)}\""
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
class ExpressionGroup
|
38
|
-
attr_reader :statements
|
39
|
-
|
40
|
-
def initialize(statements)
|
41
|
-
@statements = statements
|
42
|
-
end
|
43
|
-
|
44
|
-
def compile
|
45
|
-
statements.map(&:compile).join
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
class Expression
|
50
|
-
attr_reader :content
|
51
|
-
|
52
|
-
def initialize(content)
|
53
|
-
@content = content
|
54
|
-
end
|
55
|
-
|
56
|
-
def compile
|
57
|
-
content
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
class XmlNode
|
62
|
-
attr_reader :name, :members, :children
|
63
|
-
|
64
|
-
def initialize(name, members, children)
|
65
|
-
@name = name
|
66
|
-
@members = members || []
|
67
|
-
@children = children
|
68
|
-
end
|
69
|
-
|
70
|
-
def compile
|
71
|
-
base_tag = "rbexy_tag.#{Util.safe_tag_name(name)}(#{compile_members})"
|
72
|
-
tag = if children.length > 0
|
73
|
-
[
|
74
|
-
"#{base_tag} { capture {",
|
75
|
-
children.map(&:compile).map { |c| "@output_buffer << rbexy_prep_output(#{c})" }.join(";"),
|
76
|
-
"} }"
|
77
|
-
].join
|
78
|
-
else
|
79
|
-
base_tag
|
80
|
-
end + ".html_safe"
|
81
|
-
|
82
|
-
if Rbexy.configuration.enable_context
|
83
|
-
[
|
84
|
-
"(",
|
85
|
-
"rbexy_context.push({});",
|
86
|
-
"#{tag}.tap { rbexy_context.pop }",
|
87
|
-
")"
|
88
|
-
].join
|
89
|
-
else
|
90
|
-
tag
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def compile_members
|
95
|
-
members.each_with_object("") do |member, result|
|
96
|
-
case member
|
97
|
-
when ExpressionGroup
|
98
|
-
result << "**#{member.compile},"
|
99
|
-
when SilentNewline
|
100
|
-
result << member.compile
|
101
|
-
else
|
102
|
-
result << "#{member.compile},"
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
class XmlAttr
|
109
|
-
attr_reader :name, :value
|
110
|
-
|
111
|
-
def initialize(name, value)
|
112
|
-
@name = name
|
113
|
-
@value = value
|
114
|
-
end
|
115
|
-
|
116
|
-
def compile
|
117
|
-
"\"#{name}\": #{value.compile}"
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
class SilentNewline
|
122
|
-
def compile
|
123
|
-
"\n"
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
class Declaration
|
128
|
-
attr_reader :content
|
129
|
-
|
130
|
-
def initialize(content)
|
131
|
-
@content = content
|
132
|
-
end
|
133
|
-
|
134
|
-
def compile
|
135
|
-
"\"#{Util.safe_string(content)}\".html_safe"
|
136
|
-
end
|
137
|
-
end
|
3
|
+
autoload :Util, "rbexy/nodes/util"
|
4
|
+
autoload :AbstractNode, "rbexy/nodes/abstract_node"
|
5
|
+
autoload :Root, "rbexy/nodes/root"
|
6
|
+
autoload :Raw, "rbexy/nodes/raw"
|
7
|
+
autoload :Text, "rbexy/nodes/text"
|
8
|
+
autoload :ExpressionGroup, "rbexy/nodes/expression_group"
|
9
|
+
autoload :Expression, "rbexy/nodes/expression"
|
10
|
+
autoload :AbstractElement, "rbexy/nodes/abstract_element"
|
11
|
+
autoload :HTMLElement, "rbexy/nodes/html_element"
|
12
|
+
autoload :ComponentElement, "rbexy/nodes/component_element"
|
13
|
+
autoload :AbstractAttr, "rbexy/nodes/abstract_attr"
|
14
|
+
autoload :HTMLAttr, "rbexy/nodes/html_attr"
|
15
|
+
autoload :ComponentProp, "rbexy/nodes/component_prop"
|
16
|
+
autoload :Newline, "rbexy/nodes/newline"
|
17
|
+
autoload :Declaration, "rbexy/nodes/declaration"
|
138
18
|
end
|
139
19
|
end
|