xrb 0.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/bake/xrb/entities.rb +60 -0
- data/bake/xrb/parsers.rb +69 -0
- data/ext/extconf.rb +21 -0
- data/ext/xrb/escape.c +152 -0
- data/ext/xrb/escape.h +15 -0
- data/ext/xrb/markup.c +1949 -0
- data/ext/xrb/markup.h +6 -0
- data/ext/xrb/markup.rl +226 -0
- data/ext/xrb/query.c +619 -0
- data/ext/xrb/query.h +6 -0
- data/ext/xrb/query.rl +82 -0
- data/ext/xrb/tag.c +204 -0
- data/ext/xrb/tag.h +21 -0
- data/ext/xrb/template.c +1114 -0
- data/ext/xrb/template.h +6 -0
- data/ext/xrb/template.rl +77 -0
- data/ext/xrb/xrb.c +72 -0
- data/ext/xrb/xrb.h +132 -0
- data/lib/xrb/buffer.rb +103 -0
- data/lib/xrb/builder.rb +222 -0
- data/lib/xrb/entities.rb +2137 -0
- data/lib/xrb/entities.xrb +30 -0
- data/lib/xrb/error.rb +81 -0
- data/lib/xrb/fallback/markup.rb +1658 -0
- data/lib/xrb/fallback/markup.rl +228 -0
- data/lib/xrb/fallback/query.rb +548 -0
- data/lib/xrb/fallback/query.rl +88 -0
- data/lib/xrb/fallback/template.rb +829 -0
- data/lib/xrb/fallback/template.rl +80 -0
- data/lib/xrb/markup.rb +56 -0
- data/lib/xrb/native.rb +15 -0
- data/lib/xrb/parse_delegate.rb +19 -0
- data/lib/xrb/parsers.rb +17 -0
- data/lib/xrb/query.rb +80 -0
- data/lib/xrb/reference.rb +108 -0
- data/lib/xrb/strings.rb +47 -0
- data/lib/xrb/tag.rb +115 -0
- data/lib/xrb/template.rb +164 -0
- data/lib/xrb/uri.rb +100 -0
- data/lib/xrb/version.rb +8 -0
- data/lib/xrb.rb +11 -0
- data/license.md +23 -0
- data/readme.md +29 -0
- data.tar.gz.sig +0 -0
- metadata +109 -58
- metadata.gz.sig +0 -0
- data/README +0 -60
- data/app/helpers/ui_helper.rb +0 -80
- data/app/models/xrb/element.rb +0 -9
- data/lib/xrb/engine.rb +0 -4
- data/rails/init.rb +0 -1
- data/xrb.gemspec +0 -12
data/ext/xrb/template.h
ADDED
data/ext/xrb/template.rl
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
|
2
|
+
#include "template.h"
|
3
|
+
|
4
|
+
%%{
|
5
|
+
machine XRB_template_parser;
|
6
|
+
|
7
|
+
action instruction_begin {
|
8
|
+
instruction.begin = p;
|
9
|
+
}
|
10
|
+
|
11
|
+
action instruction_end {
|
12
|
+
instruction.end = p;
|
13
|
+
}
|
14
|
+
|
15
|
+
action emit_instruction {
|
16
|
+
rb_funcall(delegate, id_instruction, 1, XRB_Token_string(instruction, encoding));
|
17
|
+
}
|
18
|
+
|
19
|
+
action emit_instruction_line {
|
20
|
+
rb_funcall(delegate, id_instruction, 2, XRB_Token_string(instruction, encoding), newline);
|
21
|
+
}
|
22
|
+
|
23
|
+
action instruction_error {
|
24
|
+
XRB_raise_error("failed to parse instruction", buffer, p-s);
|
25
|
+
}
|
26
|
+
|
27
|
+
action expression_begin {
|
28
|
+
expression.begin = p;
|
29
|
+
}
|
30
|
+
|
31
|
+
action expression_end {
|
32
|
+
expression.end = p;
|
33
|
+
}
|
34
|
+
|
35
|
+
action emit_expression {
|
36
|
+
rb_funcall(delegate, id_expression, 1, XRB_Token_string(expression, encoding));
|
37
|
+
}
|
38
|
+
|
39
|
+
action expression_error {
|
40
|
+
XRB_raise_error("failed to parse expression", buffer, p-s);
|
41
|
+
}
|
42
|
+
|
43
|
+
action emit_text {
|
44
|
+
rb_funcall(delegate, id_text, 1, XRB_string(ts, te, encoding));
|
45
|
+
}
|
46
|
+
|
47
|
+
include template "xrb/template.rl";
|
48
|
+
|
49
|
+
write data;
|
50
|
+
}%%
|
51
|
+
|
52
|
+
VALUE XRB_Native_parse_template(VALUE self, VALUE buffer, VALUE delegate) {
|
53
|
+
VALUE string = rb_funcall(buffer, id_read, 0);
|
54
|
+
|
55
|
+
rb_encoding *encoding = rb_enc_get(string);
|
56
|
+
|
57
|
+
VALUE newline = rb_obj_freeze(rb_enc_str_new("\n", 1, encoding));
|
58
|
+
|
59
|
+
const char *s, *p, *pe, *eof, *ts, *te;
|
60
|
+
unsigned long cs, act, top = 0, stack[32] = {0};
|
61
|
+
|
62
|
+
XRB_Token expression = {0}, instruction = {0};
|
63
|
+
|
64
|
+
s = p = RSTRING_PTR(string);
|
65
|
+
eof = pe = p + RSTRING_LEN(string);
|
66
|
+
|
67
|
+
%%{
|
68
|
+
write init;
|
69
|
+
write exec;
|
70
|
+
}%%
|
71
|
+
|
72
|
+
if (p != eof) {
|
73
|
+
XRB_raise_error("could not parse all input", buffer, p-s);
|
74
|
+
}
|
75
|
+
|
76
|
+
return Qnil;
|
77
|
+
}
|
data/ext/xrb/xrb.c
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
|
2
|
+
#include "xrb.h"
|
3
|
+
|
4
|
+
#include "markup.h"
|
5
|
+
#include "template.h"
|
6
|
+
#include "query.h"
|
7
|
+
#include "tag.h"
|
8
|
+
#include "escape.h"
|
9
|
+
|
10
|
+
VALUE rb_XRB = Qnil, rb_XRB_Native = Qnil, rb_XRB_Tag = Qnil, rb_XRB_Markup = Qnil, rb_XRB_MarkupString = Qnil, rb_XRB_ParseError = Qnil;
|
11
|
+
ID id_cdata, id_open_tag_begin, id_open_tag_end, id_attribute, id_close_tag, id_text, id_doctype, id_comment, id_instruction, id_read, id_expression, id_key_get, id_string, id_integer, id_append, id_assign, id_pair, id_new, id_name, id_attributes, id_closed, id_to_s, id_is_a;
|
12
|
+
|
13
|
+
void XRB_raise_error(const char * message, VALUE buffer, size_t offset) {
|
14
|
+
VALUE exception = rb_funcall(rb_XRB_ParseError, id_new, 3, rb_str_new_cstr(message), buffer, ULONG2NUM(offset));
|
15
|
+
|
16
|
+
rb_exc_raise(exception);
|
17
|
+
}
|
18
|
+
|
19
|
+
void Init_XRB_Extension() {
|
20
|
+
id_open_tag_begin = rb_intern("open_tag_begin");
|
21
|
+
id_open_tag_end = rb_intern("open_tag_end");
|
22
|
+
id_close_tag = rb_intern("close_tag");
|
23
|
+
|
24
|
+
id_cdata = rb_intern("cdata");
|
25
|
+
id_attribute = rb_intern("attribute");
|
26
|
+
id_comment = rb_intern("comment");
|
27
|
+
id_text = rb_intern("text");
|
28
|
+
id_doctype = rb_intern("doctype");
|
29
|
+
id_instruction = rb_intern("instruction");
|
30
|
+
id_expression = rb_intern("expression");
|
31
|
+
|
32
|
+
id_read = rb_intern("read");
|
33
|
+
id_new = rb_intern("new");
|
34
|
+
|
35
|
+
id_name = rb_intern("name");
|
36
|
+
id_attributes = rb_intern("attributes");
|
37
|
+
id_closed = rb_intern("closed");
|
38
|
+
|
39
|
+
id_key_get = rb_intern("[]");
|
40
|
+
|
41
|
+
id_string = rb_intern("string");
|
42
|
+
id_integer = rb_intern("integer");
|
43
|
+
id_append = rb_intern("append");
|
44
|
+
id_assign = rb_intern("assign");
|
45
|
+
id_pair = rb_intern("pair");
|
46
|
+
|
47
|
+
id_to_s = rb_intern("to_s");
|
48
|
+
id_is_a = rb_intern("is_a?");
|
49
|
+
|
50
|
+
rb_XRB = rb_define_module("XRB");
|
51
|
+
rb_gc_register_mark_object(rb_XRB);
|
52
|
+
|
53
|
+
rb_XRB_Markup = rb_define_module_under(rb_XRB, "Markup");
|
54
|
+
rb_gc_register_mark_object(rb_XRB_Markup);
|
55
|
+
|
56
|
+
rb_XRB_Native = rb_define_module_under(rb_XRB, "Native");
|
57
|
+
rb_gc_register_mark_object(rb_XRB_Native);
|
58
|
+
|
59
|
+
Init_xrb_escape();
|
60
|
+
|
61
|
+
rb_XRB_ParseError = rb_const_get_at(rb_XRB, rb_intern("ParseError"));
|
62
|
+
rb_gc_register_mark_object(rb_XRB_ParseError);
|
63
|
+
|
64
|
+
rb_define_module_function(rb_XRB_Native, "parse_markup", XRB_Native_parse_markup, 3);
|
65
|
+
rb_define_module_function(rb_XRB_Native, "parse_template", XRB_Native_parse_template, 2);
|
66
|
+
rb_define_module_function(rb_XRB_Native, "parse_query", XRB_Native_parse_query, 2);
|
67
|
+
|
68
|
+
rb_XRB_Tag = rb_const_get_at(rb_XRB, rb_intern("Tag"));
|
69
|
+
rb_gc_register_mark_object(rb_XRB_Tag);
|
70
|
+
|
71
|
+
Init_xrb_tag();
|
72
|
+
}
|
data/ext/xrb/xrb.h
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
|
2
|
+
#pragma once
|
3
|
+
|
4
|
+
#include "ruby.h"
|
5
|
+
#include <ruby/encoding.h>
|
6
|
+
|
7
|
+
// Used to efficiently convert symbols to strings (e.g. tag attribute keys).
|
8
|
+
#ifndef HAVE_RB_SYM2STR
|
9
|
+
#define rb_sym2str(sym) rb_id2str(SYM2ID(sym))
|
10
|
+
#endif
|
11
|
+
|
12
|
+
// Consistent and meaningful append cstring to ruby string/buffer.
|
13
|
+
#ifndef HAVE_RB_STR_CAT_CSTR
|
14
|
+
#define rb_str_cat_cstr rb_str_cat2
|
15
|
+
#endif
|
16
|
+
|
17
|
+
// Prefer non-generic macro names where possible.
|
18
|
+
#ifndef RB_IMMEDIATE_P
|
19
|
+
#define RB_IMMEDIATE_P IMMEDIATE_P
|
20
|
+
#endif
|
21
|
+
|
22
|
+
// A helper to reserve a specific capacity of data for a buffer.
|
23
|
+
#ifndef HAVE_RB_STR_RESERVE
|
24
|
+
inline VALUE rb_str_reserve(VALUE string, long extra) {
|
25
|
+
long actual = RSTRING_LEN(string);
|
26
|
+
rb_str_resize(string, actual + extra);
|
27
|
+
rb_str_set_len(string, actual);
|
28
|
+
return string;
|
29
|
+
}
|
30
|
+
#endif
|
31
|
+
|
32
|
+
// Modules and classes exposed by XRB.
|
33
|
+
extern VALUE
|
34
|
+
rb_XRB,
|
35
|
+
rb_XRB_Markup,
|
36
|
+
rb_XRB_Tag,
|
37
|
+
rb_XRB_MarkupString,
|
38
|
+
rb_XRB_Native,
|
39
|
+
rb_XRB_ParseError;
|
40
|
+
|
41
|
+
// Symbols used for delegate callbacks and general function calls.
|
42
|
+
extern ID
|
43
|
+
id_cdata,
|
44
|
+
id_open_tag_begin,
|
45
|
+
id_open_tag_end,
|
46
|
+
id_attribute,
|
47
|
+
id_close_tag,
|
48
|
+
id_text,
|
49
|
+
id_doctype,
|
50
|
+
id_comment,
|
51
|
+
id_instruction,
|
52
|
+
id_read,
|
53
|
+
id_expression,
|
54
|
+
id_key_get,
|
55
|
+
id_new,
|
56
|
+
id_name,
|
57
|
+
id_integer,
|
58
|
+
id_string,
|
59
|
+
id_append,
|
60
|
+
id_assign,
|
61
|
+
id_pair,
|
62
|
+
id_attributes,
|
63
|
+
id_closed,
|
64
|
+
id_to_s,
|
65
|
+
id_is_a;
|
66
|
+
|
67
|
+
// A convenient C string token class.
|
68
|
+
typedef struct {
|
69
|
+
const char * begin;
|
70
|
+
const char * end;
|
71
|
+
} XRB_Token;
|
72
|
+
|
73
|
+
// Convert a token to a Ruby string.
|
74
|
+
static inline VALUE XRB_Token_string(XRB_Token token, rb_encoding * encoding) {
|
75
|
+
return rb_enc_str_new(token.begin, token.end - token.begin, encoding);
|
76
|
+
}
|
77
|
+
|
78
|
+
// Convert a C string to a Ruby string.
|
79
|
+
static inline VALUE XRB_string(const char * begin, const char * end, rb_encoding * encoding) {
|
80
|
+
return rb_enc_str_new(begin, end - begin, encoding);
|
81
|
+
}
|
82
|
+
|
83
|
+
// Create an empty buffer for the given input string.
|
84
|
+
static inline VALUE XRB_buffer_for(VALUE string) {
|
85
|
+
VALUE buffer = rb_enc_str_new(0, 0, rb_enc_get(string));
|
86
|
+
|
87
|
+
rb_str_reserve(buffer, RSTRING_LEN(string) + 128);
|
88
|
+
|
89
|
+
return buffer;
|
90
|
+
}
|
91
|
+
|
92
|
+
// Raise a parse error for the given input buffer at a specific offset.
|
93
|
+
NORETURN(void XRB_raise_error(const char * message, VALUE buffer, size_t offset));
|
94
|
+
|
95
|
+
// Append a string to a buffer. The buffer may or may not be initialized.
|
96
|
+
static inline void XRB_append(VALUE * buffer, rb_encoding * encoding, VALUE string) {
|
97
|
+
if (*buffer == Qnil) {
|
98
|
+
*buffer = rb_enc_str_new(0, 0, encoding);
|
99
|
+
}
|
100
|
+
|
101
|
+
rb_str_concat(*buffer, string);
|
102
|
+
}
|
103
|
+
|
104
|
+
// Append a token to a buffer. The buffer may or may not be initialized.
|
105
|
+
static inline void XRB_append_token(VALUE * buffer, rb_encoding * encoding, XRB_Token token) {
|
106
|
+
if (*buffer == Qnil) {
|
107
|
+
// Allocate a buffer exactly the right size:
|
108
|
+
*buffer = rb_enc_str_new(token.begin, token.end - token.begin, encoding);
|
109
|
+
} else {
|
110
|
+
// Append the characters to the existing buffer:
|
111
|
+
rb_str_buf_cat(*buffer, token.begin, token.end - token.begin);
|
112
|
+
}
|
113
|
+
}
|
114
|
+
|
115
|
+
// Append a (unicode) codepoint to a buffer. The buffer may or may not be initialized.
|
116
|
+
static inline void XRB_append_codepoint(VALUE * buffer, rb_encoding * encoding, unsigned long codepoint) {
|
117
|
+
if (*buffer == Qnil) {
|
118
|
+
*buffer = rb_enc_str_new(0, 0, encoding);
|
119
|
+
}
|
120
|
+
|
121
|
+
rb_str_concat(*buffer, ULONG2NUM(codepoint));
|
122
|
+
}
|
123
|
+
|
124
|
+
// Convert the class of a string if there were no entities detected.
|
125
|
+
static inline VALUE XRB_markup_safe(VALUE string, unsigned has_entities) {
|
126
|
+
if (!has_entities) {
|
127
|
+
// Apparently should not use this to change klass, but it's exactly what we need here to make things lightning fast.
|
128
|
+
rb_obj_reveal(string, rb_XRB_MarkupString);
|
129
|
+
}
|
130
|
+
|
131
|
+
return string;
|
132
|
+
}
|
data/lib/xrb/buffer.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2016-2024, by Samuel Williams.
|
5
|
+
|
6
|
+
module XRB
|
7
|
+
class Buffer
|
8
|
+
def initialize(string, path: '<string>')
|
9
|
+
@string = string
|
10
|
+
@path = path
|
11
|
+
end
|
12
|
+
|
13
|
+
attr :path
|
14
|
+
|
15
|
+
def encoding
|
16
|
+
@string.encoding
|
17
|
+
end
|
18
|
+
|
19
|
+
def read
|
20
|
+
@string
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.load_file(path)
|
24
|
+
FileBuffer.new(path).freeze
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.load(string)
|
28
|
+
Buffer.new(string).freeze
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_buffer
|
32
|
+
self
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class FileBuffer
|
37
|
+
def initialize(path)
|
38
|
+
@path = path
|
39
|
+
end
|
40
|
+
|
41
|
+
def freeze
|
42
|
+
return self if frozen?
|
43
|
+
|
44
|
+
read
|
45
|
+
|
46
|
+
super
|
47
|
+
end
|
48
|
+
|
49
|
+
attr :path
|
50
|
+
|
51
|
+
def encoding
|
52
|
+
read.encoding
|
53
|
+
end
|
54
|
+
|
55
|
+
def read
|
56
|
+
@cache ||= File.read(@path).freeze
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_buffer
|
60
|
+
Buffer.new(self.read, @path)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class IOBuffer
|
65
|
+
def initialize(io, path: io.inspect)
|
66
|
+
@io = io
|
67
|
+
@path = path
|
68
|
+
end
|
69
|
+
|
70
|
+
def freeze
|
71
|
+
return self if frozen?
|
72
|
+
|
73
|
+
read
|
74
|
+
|
75
|
+
super
|
76
|
+
end
|
77
|
+
|
78
|
+
attr :path
|
79
|
+
|
80
|
+
def encoding
|
81
|
+
read.encoding
|
82
|
+
end
|
83
|
+
|
84
|
+
def read
|
85
|
+
@cache ||= @io.read.freeze
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_buffer
|
89
|
+
Buffer.new(self.read, path: @path)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.Buffer(value)
|
94
|
+
case value
|
95
|
+
when String
|
96
|
+
Buffer.new(value)
|
97
|
+
when Buffer, FileBuffer, IOBuffer
|
98
|
+
value
|
99
|
+
else
|
100
|
+
value.to_buffer
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/lib/xrb/builder.rb
ADDED
@@ -0,0 +1,222 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2012-2024, by Samuel Williams.
|
5
|
+
|
6
|
+
require_relative 'markup'
|
7
|
+
require_relative 'tag'
|
8
|
+
|
9
|
+
module XRB
|
10
|
+
# Build markup quickly and efficiently.
|
11
|
+
class Builder
|
12
|
+
include Markup
|
13
|
+
|
14
|
+
INDENT = "\t"
|
15
|
+
|
16
|
+
class Fragment
|
17
|
+
def initialize(block)
|
18
|
+
@block = block
|
19
|
+
@builder = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def call(builder)
|
23
|
+
@block.call(builder)
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
unless @builder
|
28
|
+
@builder = Builder.new
|
29
|
+
|
30
|
+
self.call(@builder)
|
31
|
+
end
|
32
|
+
|
33
|
+
return @builder.to_s
|
34
|
+
end
|
35
|
+
|
36
|
+
def == other
|
37
|
+
# This is a bit of a hack... but is required for existing specs to pass:
|
38
|
+
self.to_s == other.to_s
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# A helper to generate fragments of markup.
|
43
|
+
def self.fragment(output = nil, &block)
|
44
|
+
if output.is_a?(Binding)
|
45
|
+
output = Template.buffer(output)
|
46
|
+
end
|
47
|
+
|
48
|
+
if output.nil?
|
49
|
+
return Fragment.new(block)
|
50
|
+
end
|
51
|
+
|
52
|
+
if output.is_a?(Builder)
|
53
|
+
block.call(output)
|
54
|
+
else
|
55
|
+
block.call(Builder.new(output))
|
56
|
+
end
|
57
|
+
|
58
|
+
return nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.tag(name, content, **attributes)
|
62
|
+
self.fragment do |builder|
|
63
|
+
builder.inline(name, attributes) do
|
64
|
+
builder.text(content)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def initialize(output = nil, indent: true, encoding: Encoding::UTF_8)
|
70
|
+
# This field gets togged in #inline so we keep track of it separately from @indentation.
|
71
|
+
@indent = indent
|
72
|
+
|
73
|
+
# We don't need to use MarkupString here as Builder itself is considered markup and should be inserted directly into the output stream.
|
74
|
+
@output = output || MarkupString.new.force_encoding(encoding)
|
75
|
+
|
76
|
+
@level = [0]
|
77
|
+
@children = [0]
|
78
|
+
end
|
79
|
+
|
80
|
+
attr :output
|
81
|
+
|
82
|
+
def encoding
|
83
|
+
@output.encoding
|
84
|
+
end
|
85
|
+
|
86
|
+
# Required for output to buffer.
|
87
|
+
def to_str
|
88
|
+
@output
|
89
|
+
end
|
90
|
+
|
91
|
+
alias to_s to_str
|
92
|
+
|
93
|
+
def == other
|
94
|
+
@output == String(other)
|
95
|
+
end
|
96
|
+
|
97
|
+
def indentation
|
98
|
+
if @indent
|
99
|
+
INDENT * (@level.size - 1)
|
100
|
+
else
|
101
|
+
''
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def doctype(attributes = 'html')
|
106
|
+
@output << "<!DOCTYPE #{attributes}>\n"
|
107
|
+
end
|
108
|
+
|
109
|
+
# Begin a block tag.
|
110
|
+
def tag(name, attributes = {}, &block)
|
111
|
+
full_tag(name, attributes, @indent, @indent, &block)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Begin an inline tag.
|
115
|
+
def inline_tag(name, attributes = {}, &block)
|
116
|
+
original_indent = @indent
|
117
|
+
|
118
|
+
full_tag(name, attributes, @indent, false) do
|
119
|
+
@indent = false
|
120
|
+
yield if block_given?
|
121
|
+
end
|
122
|
+
ensure
|
123
|
+
@indent = original_indent
|
124
|
+
end
|
125
|
+
|
126
|
+
alias inline inline_tag
|
127
|
+
|
128
|
+
def inline!
|
129
|
+
original_indent = @indent
|
130
|
+
@indent = false
|
131
|
+
|
132
|
+
yield
|
133
|
+
ensure
|
134
|
+
@indent = original_indent
|
135
|
+
end
|
136
|
+
|
137
|
+
def text(content)
|
138
|
+
return unless content
|
139
|
+
|
140
|
+
if @indent
|
141
|
+
@output << "\n" if @level.last > 0
|
142
|
+
@output << indentation
|
143
|
+
end
|
144
|
+
|
145
|
+
Markup.append(@output, content)
|
146
|
+
|
147
|
+
if @indent
|
148
|
+
@output << "\n"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def raw(content)
|
153
|
+
@output << content
|
154
|
+
end
|
155
|
+
|
156
|
+
def <<(content)
|
157
|
+
return unless content
|
158
|
+
|
159
|
+
if content.is_a?(Fragment)
|
160
|
+
inline! do
|
161
|
+
content.call(self)
|
162
|
+
end
|
163
|
+
else
|
164
|
+
Markup.append(@output, content)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Append pre-existing markup:
|
169
|
+
def append(value)
|
170
|
+
return unless value
|
171
|
+
|
172
|
+
# The parent has one more child:
|
173
|
+
@level[-1] += 1
|
174
|
+
|
175
|
+
if @indent
|
176
|
+
value.each_line.with_index do |line, i|
|
177
|
+
@output << indentation << line
|
178
|
+
end
|
179
|
+
else
|
180
|
+
@output << value
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
protected
|
185
|
+
|
186
|
+
# A normal block level/container tag.
|
187
|
+
def full_tag(name, attributes, indent_outer, indent_inner, &block)
|
188
|
+
if block_given?
|
189
|
+
if indent_outer
|
190
|
+
@output << "\n" if @level.last > 0
|
191
|
+
@output << indentation
|
192
|
+
end
|
193
|
+
|
194
|
+
tag = XRB::Tag.opened(name.to_s, attributes)
|
195
|
+
tag.write_opening_tag(@output)
|
196
|
+
@output << "\n" if indent_inner
|
197
|
+
|
198
|
+
# The parent has one more child:
|
199
|
+
@level[-1] += 1
|
200
|
+
|
201
|
+
@level << 0
|
202
|
+
|
203
|
+
yield
|
204
|
+
|
205
|
+
children = @level.pop
|
206
|
+
|
207
|
+
if indent_inner
|
208
|
+
@output << "\n" if children > 0
|
209
|
+
@output << indentation
|
210
|
+
end
|
211
|
+
|
212
|
+
tag.write_closing_tag(@output)
|
213
|
+
else
|
214
|
+
# The parent has one more child:
|
215
|
+
@level[-1] += 1
|
216
|
+
|
217
|
+
@output << indentation
|
218
|
+
XRB::Tag.append_tag(@output, name.to_s, attributes, nil)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|