Class: Ruby2html::Render
- Inherits:
-
Object
- Object
- Ruby2html::Render
- Defined in:
- lib/gem/ruby2html/render.rb,
ext/ruby2html/ruby2html.c
Constant Summary collapse
- HTML5_TAGS =
%w[ a abbr address area article aside audio b base bdi bdo blockquote body br button canvas caption cite code col colgroup data datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 head header hr html i iframe img input ins kbd label legend li link main map mark meta meter nav noscript object ol optgroup option output p param picture pre progress q rp rt ruby s samp script section select small source span strong style sub summary sup table tbody td template textarea tfoot th thead time title tr track u ul var video wbr turbo-frame turbo-stream ].freeze
- VOID_ELEMENTS =
%w[area base br col embed hr img input link meta param source track wbr].freeze
- COMMON_RAILS_METHOD_HELPERS =
%w[link_to image_tag form_with button_to].freeze
- METHOD_DEFINITIONS =
Pre-generate all HTML tag methods as a single string
HTML5_TAGS.map do |tag| method_name = tag.tr('-', '_') is_void = VOID_ELEMENTS.include?(tag) <<-RUBY def #{method_name}(*args, **options, &block) content = args.first.is_a?(String) ? args.shift : nil estimated_size = #{tag.length * 2 + 5} estimated_size += 32 if options.any? estimated_size += content.length if content tag_content = String.new(capacity: estimated_size) tag_content << '<#{tag}' fast_buffer_append(tag_content, fast_attributes_to_s(options)) #{if is_void 'tag_content << \' />\'' else <<-TAG_LOGIC tag_content << '>' if block prev_output = @current_output nested_content = String.new(capacity: 1024) @current_output = nested_content block_result = block.call @current_output = prev_output if block_result.is_a?(String) fast_buffer_append(tag_content, fast_escape_html(block_result)) else fast_buffer_append(tag_content, nested_content) end elsif content fast_buffer_append(tag_content, fast_escape_html(content)) end tag_content << '</#{tag}>' TAG_LOGIC end} fast_buffer_append(@current_output, tag_content) end RUBY end.join("\n")
Instance Attribute Summary collapse
-
#current_output ⇒ Object
Returns the value of attribute current_output.
-
#output ⇒ Object
readonly
Returns the value of attribute output.
Instance Method Summary collapse
- #__render_from_rails(template_path) ⇒ Object
- #component(component_output) ⇒ Object
-
#fast_attributes_to_s(hash) ⇒ Object
Fast attribute string builder.
-
#fast_buffer_append(buffer, str) ⇒ Object
Fast string buffer concatenation.
-
#fast_escape_html(str) ⇒ Object
Fast HTML escaping.
-
#initialize(context = nil, &root) ⇒ Render
constructor
A new instance of Render.
- #plain(text) ⇒ Object
- #render(*args, **options, &block) ⇒ Object
- #respond_to?(method_name, include_private = false) ⇒ Boolean
Constructor Details
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method_name, *args, **options, &block) ⇒ Object (private)
120 121 122 123 124 125 126 |
# File 'lib/gem/ruby2html/render.rb', line 120 def method_missing(method_name, *args, **, &block) if @context.respond_to?(method_name) @context.send(method_name, *args, **, &block) else super end end |
Instance Attribute Details
#current_output ⇒ Object
Returns the value of attribute current_output.
68 69 70 |
# File 'lib/gem/ruby2html/render.rb', line 68 def current_output @current_output end |
#output ⇒ Object (readonly)
Returns the value of attribute output.
67 68 69 |
# File 'lib/gem/ruby2html/render.rb', line 67 def output @output end |
Instance Method Details
#__render_from_rails(template_path) ⇒ Object
77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/gem/ruby2html/render.rb', line 77 def __render_from_rails(template_path) result = render return result unless annotate_rendered_view_with_filenames? template_path = template_path.sub("#{Rails.root}/", '') comment_start = "<!-- BEGIN #{template_path} -->" comment_end = "<!-- END #{template_path} -->" final_result = String.new(capacity: result.length + comment_start.length + comment_end.length) final_result << comment_start final_result << result final_result << comment_end final_result.html_safe end |
#component(component_output) ⇒ Object
115 116 117 |
# File 'lib/gem/ruby2html/render.rb', line 115 def component(component_output) fast_buffer_append(@current_output, component_output) end |
#fast_attributes_to_s(hash) ⇒ Object
Fast attribute string builder
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'ext/ruby2html/ruby2html.c', line 69
static VALUE fast_attributes_to_s(VALUE self, VALUE hash) {
if (NIL_P(hash) || RHASH_EMPTY_P(hash)) return rb_str_new2("");
VALUE result = rb_str_buf_new(64); // Pre-allocate with reasonable size
VALUE keys = rb_funcall(hash, rb_intern("keys"), 0);
long len = RARRAY_LEN(keys);
for (long i = 0; i < len; i++) {
VALUE key = rb_ary_entry(keys, i);
VALUE value = rb_hash_aref(hash, key);
if (!NIL_P(value)) {
rb_str_cat2(result, " ");
rb_str_append(result, rb_String(key));
rb_str_cat2(result, "=\"");
rb_str_append(result, fast_escape_html(self, rb_String(value)));
rb_str_cat2(result, "\"");
}
}
return result;
}
|
#fast_buffer_append(buffer, str) ⇒ Object
Fast string buffer concatenation
93 94 95 96 97 98 |
# File 'ext/ruby2html/ruby2html.c', line 93
static VALUE fast_buffer_append(VALUE self, VALUE buffer, VALUE str) {
if (!NIL_P(str)) {
rb_funcall(buffer, rb_intern("<<"), 1, rb_String(str));
}
return Qnil;
}
|
#fast_escape_html(str) ⇒ Object
Fast HTML escaping
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'ext/ruby2html/ruby2html.c', line 10
static VALUE fast_escape_html(VALUE self, VALUE str) {
if (NIL_P(str)) return Qnil;
str = rb_String(str);
long len = RSTRING_LEN(str);
const char *ptr = RSTRING_PTR(str);
// First pass: calculate required buffer size
long new_len = len;
for (long i = 0; i < len; i++) {
switch (ptr[i]) {
case '&': new_len += 4; break; // &
case '<': new_len += 3; break; // <
case '>': new_len += 3; break; // >
case '"': new_len += 5; break; // "
case '\'': new_len += 5; break; // '
}
}
if (new_len == len) return str;
VALUE result = rb_str_new(NULL, new_len);
char *out = RSTRING_PTR(result);
long pos = 0;
// Second pass: actual escaping
for (long i = 0; i < len; i++) {
switch (ptr[i]) {
case '&':
memcpy(out + pos, "&", 5);
pos += 5;
break;
case '<':
memcpy(out + pos, "<", 4);
pos += 4;
break;
case '>':
memcpy(out + pos, ">", 4);
pos += 4;
break;
case '"':
memcpy(out + pos, """, 6);
pos += 6;
break;
case '\'':
memcpy(out + pos, "'", 5);
pos += 5;
break;
default:
out[pos++] = ptr[i];
}
}
rb_str_set_len(result, pos);
rb_enc_associate(result, rb_enc_get(str));
return result;
}
|
#plain(text) ⇒ Object
107 108 109 110 111 112 113 |
# File 'lib/gem/ruby2html/render.rb', line 107 def plain(text) if defined?(ActiveSupport) && (text.is_a?(ActiveSupport::SafeBuffer) || text.html_safe?) fast_buffer_append(@current_output, text) else fast_buffer_append(@current_output, fast_escape_html(text.to_s)) end end |
#render(*args, **options, &block) ⇒ Object
92 93 94 95 96 97 98 99 100 101 |
# File 'lib/gem/ruby2html/render.rb', line 92 def render(*args, **, &block) set_instance_variables return plain @context.render(*args, **, &block) if !args.empty? || !.empty? || block_given? instance_exec(&@root) result = @output result = ActiveSupport::SafeBuffer.new(result) if defined?(ActiveSupport) result end |
#respond_to?(method_name, include_private = false) ⇒ Boolean
103 104 105 |
# File 'lib/gem/ruby2html/render.rb', line 103 def respond_to?(method_name, include_private = false) HTML5_TAGS.include?(method_name.to_s.tr('_', '-')) || super end |