xrb 0.1 → 0.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 +7 -0
- checksums.yaml.gz.sig +1 -0
- data/bake/xrb/entities.rb +60 -0
- data/bake/xrb/parsers.rb +66 -0
- data/ext/Makefile +270 -0
- data/ext/XRB_Extension.bundle +0 -0
- data/ext/escape.o +0 -0
- data/ext/extconf.h +5 -0
- data/ext/extconf.rb +21 -0
- data/ext/markup.o +0 -0
- data/ext/mkmf.log +122 -0
- data/ext/query.o +0 -0
- data/ext/tag.o +0 -0
- data/ext/template.o +0 -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/ext/xrb.o +0 -0
- data/lib/xrb/buffer.rb +103 -0
- data/lib/xrb/builder.rb +229 -0
- data/lib/xrb/entities.rb +2137 -0
- data/lib/xrb/entities.xrb +15 -0
- data/lib/xrb/error.rb +81 -0
- data/lib/xrb/fallback/markup.rb +1657 -0
- data/lib/xrb/fallback/markup.rl +227 -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/parsers.rb +16 -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 +128 -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 +34 -0
- data.tar.gz.sig +0 -0
- metadata +118 -58
- metadata.gz.sig +2 -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/ext/xrb.o
ADDED
Binary file
|
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,229 @@
|
|
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
|
+
|
41
|
+
def >> block
|
42
|
+
if block
|
43
|
+
Template.buffer(block.binding) << self
|
44
|
+
|
45
|
+
return nil
|
46
|
+
else
|
47
|
+
return self
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# A helper to generate fragments of markup.
|
53
|
+
def self.fragment(output = nil, &block)
|
54
|
+
if output.is_a?(Binding)
|
55
|
+
output = Template.buffer(output)
|
56
|
+
end
|
57
|
+
|
58
|
+
if output.nil?
|
59
|
+
return Fragment.new(block)
|
60
|
+
end
|
61
|
+
|
62
|
+
block.call(output)
|
63
|
+
|
64
|
+
# We explicitly return nil here as we don't want to append the output twice.
|
65
|
+
return nil
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.tag(name, content, **attributes)
|
69
|
+
self.fragment do |builder|
|
70
|
+
builder.inline(name, attributes) do
|
71
|
+
builder.text(content)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def initialize(output = nil, indent: true, encoding: Encoding::UTF_8)
|
77
|
+
# This field gets togged in #inline so we keep track of it separately from @indentation.
|
78
|
+
@indent = indent
|
79
|
+
|
80
|
+
# We don't need to use MarkupString here as Builder itself is considered markup and should be inserted directly into the output stream.
|
81
|
+
@output = output || MarkupString.new.force_encoding(encoding)
|
82
|
+
|
83
|
+
@level = [0]
|
84
|
+
@children = [0]
|
85
|
+
end
|
86
|
+
|
87
|
+
attr :output
|
88
|
+
|
89
|
+
def encoding
|
90
|
+
@output.encoding
|
91
|
+
end
|
92
|
+
|
93
|
+
# Required for output to buffer.
|
94
|
+
def to_str
|
95
|
+
@output
|
96
|
+
end
|
97
|
+
|
98
|
+
alias to_s to_str
|
99
|
+
|
100
|
+
def == other
|
101
|
+
@output == String(other)
|
102
|
+
end
|
103
|
+
|
104
|
+
def indentation
|
105
|
+
if @indent
|
106
|
+
INDENT * (@level.size - 1)
|
107
|
+
else
|
108
|
+
''
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def doctype(attributes = 'html')
|
113
|
+
@output << "<!doctype #{attributes}>\n"
|
114
|
+
end
|
115
|
+
|
116
|
+
# Begin a block tag.
|
117
|
+
def tag(name, attributes = {}, &block)
|
118
|
+
full_tag(name, attributes, @indent, @indent, &block)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Begin an inline tag.
|
122
|
+
def inline_tag(name, attributes = {}, &block)
|
123
|
+
original_indent = @indent
|
124
|
+
|
125
|
+
full_tag(name, attributes, @indent, false) do
|
126
|
+
@indent = false
|
127
|
+
yield if block_given?
|
128
|
+
end
|
129
|
+
ensure
|
130
|
+
@indent = original_indent
|
131
|
+
end
|
132
|
+
|
133
|
+
alias inline inline_tag
|
134
|
+
|
135
|
+
def inline!
|
136
|
+
original_indent = @indent
|
137
|
+
@indent = false
|
138
|
+
|
139
|
+
yield
|
140
|
+
ensure
|
141
|
+
@indent = original_indent
|
142
|
+
end
|
143
|
+
|
144
|
+
def text(content)
|
145
|
+
return unless content
|
146
|
+
|
147
|
+
if @indent
|
148
|
+
@output << "\n" if @level.last > 0
|
149
|
+
@output << indentation
|
150
|
+
end
|
151
|
+
|
152
|
+
Markup.append(@output, content)
|
153
|
+
|
154
|
+
if @indent
|
155
|
+
@output << "\n"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def raw(content)
|
160
|
+
@output << content
|
161
|
+
end
|
162
|
+
|
163
|
+
def <<(content)
|
164
|
+
return unless content
|
165
|
+
|
166
|
+
if content.is_a?(Fragment)
|
167
|
+
inline! do
|
168
|
+
content.call(self)
|
169
|
+
end
|
170
|
+
else
|
171
|
+
Markup.append(@output, content)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Append pre-existing markup:
|
176
|
+
def append(value)
|
177
|
+
return unless value
|
178
|
+
|
179
|
+
# The parent has one more child:
|
180
|
+
@level[-1] += 1
|
181
|
+
|
182
|
+
if @indent
|
183
|
+
value.each_line.with_index do |line, i|
|
184
|
+
@output << indentation << line
|
185
|
+
end
|
186
|
+
else
|
187
|
+
@output << value
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
protected
|
192
|
+
|
193
|
+
# A normal block level/container tag.
|
194
|
+
def full_tag(name, attributes, indent_outer, indent_inner, &block)
|
195
|
+
if block_given?
|
196
|
+
if indent_outer
|
197
|
+
@output << "\n" if @level.last > 0
|
198
|
+
@output << indentation
|
199
|
+
end
|
200
|
+
|
201
|
+
tag = XRB::Tag.opened(name.to_s, attributes)
|
202
|
+
tag.write_opening_tag(@output)
|
203
|
+
@output << "\n" if indent_inner
|
204
|
+
|
205
|
+
# The parent has one more child:
|
206
|
+
@level[-1] += 1
|
207
|
+
|
208
|
+
@level << 0
|
209
|
+
|
210
|
+
yield
|
211
|
+
|
212
|
+
children = @level.pop
|
213
|
+
|
214
|
+
if indent_inner
|
215
|
+
@output << "\n" if children > 0
|
216
|
+
@output << indentation
|
217
|
+
end
|
218
|
+
|
219
|
+
tag.write_closing_tag(@output)
|
220
|
+
else
|
221
|
+
# The parent has one more child:
|
222
|
+
@level[-1] += 1
|
223
|
+
|
224
|
+
@output << indentation
|
225
|
+
XRB::Tag.append_tag(@output, name.to_s, attributes, nil)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|