berns 3.2.1 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 74e861ab5e3bfb8359f063dc7637b4f9c8f30ed60722b3f6cfec7aaf432915af
4
- data.tar.gz: 17e884c022d55c44ad4ff080ec485d82688664529aee4d656b95d1d93a129e31
3
+ metadata.gz: bcded7fea247763fc7b27807c2c945943e0f08895b117ae086e4041bf1b08371
4
+ data.tar.gz: 965518819feb3eef689f19b7cc5470a973198b891e00f9d7f8ac7fd5b4620119
5
5
  SHA512:
6
- metadata.gz: 17166b6a903fba75f3cf3984710735e92206892eec6310bb2144ff5e088694089326426baf2c8e7c1246e681d4887559f01f7364635bfc29006878a908b9912f
7
- data.tar.gz: 9472135003033d1fdc6db413e44cf7ab69be6fa947b9e2a452d2bd24f316f0708055d03d6157adf292c5b9644d73dd08cad261c1b4c45588be18b8069e5366e2
6
+ metadata.gz: f023bfcde5d5b272477d2dd7e7c2d6de430c4ec544d6a10da75f7ff72b5980349d282710796a2ac5c4bede5e72df5456dd733828c7869f3f1231b26314f450e4
7
+ data.tar.gz: d606bdeed6b199fa467c4f80236d9dab40ef0db13e3105397bbe411caffa71c00274ea5902e1ce3f4764ab915cada0a9574d133905f07e28a348e2e28f9b7cfa
data/LICENSE.txt CHANGED
@@ -1,21 +1,20 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2018 Taylor Beck
3
+ Copyright © 2021 Taylor Beck and Evan Lecklider
4
4
 
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the Software), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
11
 
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
14
 
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.
15
+ THE SOFTWARE IS PROVIDED AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.org CHANGED
@@ -1,6 +1,7 @@
1
1
  * Berns
2
2
 
3
3
  [[https://badge.fury.io/rb/berns][https://badge.fury.io/rb/berns.svg]]
4
+ [[https://github.com/evanleck/berns/actions/workflows/main.yml][https://github.com/evanleck/berns/actions/workflows/main.yml/badge.svg]]
4
5
 
5
6
  A utility library for generating HTML strings.
6
7
 
@@ -95,10 +96,84 @@ Note that this is an extremely naive implementation of HTML sanitization that
95
96
  literally just looks for "<" and ">" characters and removes the contents between
96
97
  them. This should probably only be used on trusted strings.
97
98
 
99
+ *** =build { content }=
100
+
101
+ The =build= method uses =Berns::Builder= to let you create HTML strings using a DSL.
102
+
103
+ #+begin_src ruby
104
+ Berns.build { h1 { 'Heading' } } # => '<h1>Heading</h1>'
105
+ #+end_src
106
+
107
+ See below for more on =Berns::Builder=.
108
+
109
+ *** =Berns::Builder= HTML DSL
110
+
111
+ Added in version 3.4.0 and heavily inspired by the likes of [[https://github.com/digital-fabric/papercraft][Papercraft]], [[https://github.com/markaby/markaby][Markaby]],
112
+ and [[https://github.com/activeadmin/arbre][Arbre]], the =Berns::Builder= class lets you create HTML strings using a DSL.
113
+
114
+ #+begin_src ruby
115
+ template = Berns::Builder.new do
116
+ h1 { 'Heading' }
117
+ p(class: 'paragraph') do
118
+ text 'Bare text here.'
119
+
120
+ b { 'Bold text here' }
121
+ end
122
+ end
123
+ #+end_src
124
+
125
+ Within the block provided to =Berns::Builder.new= every standard element method,
126
+ void element method, =#element=, and =#void= are available as methods and each
127
+ time you use one of those methods the result is appended to an internal buffer.
128
+ In addition, the =#text= method can be used to append a plain text string to the
129
+ buffer and that text will be HTML escaped, so it's good for untrusted content.
130
+
131
+ Once initialized, rendering the template to a string can be done with the
132
+ =#call= method and any arguments, positional or keyword, will be passed through
133
+ as-is to the block provided to =#new=.
134
+
135
+ #+begin_src ruby
136
+ string = template.call # =>
137
+ # <h1>
138
+ # Heading
139
+ # </h1>
140
+ # <p class='paragraph'>
141
+ # Bare text here.
142
+ # <b>
143
+ # Bold text here.
144
+ # </b>
145
+ # </p>
146
+ #+end_src
147
+
148
+ In addition to initializing a new instance of =Berns::Builder=, you can
149
+ construct and render a template to a string all at once with =Berns.build=.
150
+
151
+ #+begin_src ruby
152
+ Berns.build do
153
+ h1 { 'Heading' }
154
+ p(class: 'paragraph') do
155
+ text 'Bare text here.'
156
+
157
+ b { 'Bold text here' }
158
+ end
159
+ end # =>
160
+ # <h1>
161
+ # Heading
162
+ # </h1>
163
+ # <p class='paragraph'>
164
+ # Bare text here.
165
+ # <b>
166
+ # Bold text here.
167
+ # </b>
168
+ # </p>
169
+ #+end_src
170
+
98
171
  *** Standard and void elements
99
172
 
100
173
  All standard and void HTML elements are defined as methods on Berns, so you can
101
- create e.g. a link with =Berns.a=. Below is the full list of standard elements.
174
+ create e.g. a link with =Berns.a=. Below is the full list of standard elements
175
+ which are also available in the constant =Berns::STANDARD= as an array of
176
+ symbols.
102
177
 
103
178
  #+begin_example
104
179
  a abbr address article aside audio b bdi bdo blockquote body button
@@ -111,10 +186,33 @@ table tbody td template textarea tfoot th thead time title tr u ul var
111
186
  video
112
187
  #+end_example
113
188
 
114
-
115
189
  Below is the full list of void elements that are defined as singleton methods on
116
- Berns.
190
+ Berns which are also available in the constant =Berns::VOID= as an array of
191
+ symbols.
117
192
 
118
193
  #+begin_example
119
194
  area base br col embed hr img input link menuitem meta param source track wbr
120
195
  #+end_example
196
+
197
+ ** Performance
198
+
199
+ Berns 3 is about three times faster than the pure Ruby implementation used in
200
+ version 2. See the file [[file:benchmarks/performance.rb][benchmarks/performance.rb]] for the benchmark code.
201
+
202
+ #+begin_example
203
+ Warming up --------------------------------------
204
+ element 27.373k i/100ms
205
+ berns 94.118k i/100ms
206
+ Calculating -------------------------------------
207
+ element 314.078k (± 4.5%) i/s - 1.588M in 5.065539s
208
+ berns 935.528k (± 6.1%) i/s - 4.706M in 5.049718s
209
+
210
+ Comparison:
211
+ berns: 935527.9 i/s
212
+ element: 314078.4 i/s - 2.98x (± 0.00) slower
213
+ #+end_example
214
+
215
+ ** Trivia
216
+
217
+ The name "Berns" is taken from the name of [[https://en.wikipedia.org/wiki/HTML#Development][the inventor of HTML]],
218
+ [[https://en.wikipedia.org/wiki/Tim_Berners-Lee][Sir Tim Berners-Lee]].
data/ext/berns/berns.c CHANGED
@@ -64,7 +64,7 @@ static const size_t sllen = 1;
64
64
  static VALUE external_##element_name##_element(int argc, VALUE *argv, RB_UNUSED_VAR(VALUE self)) { \
65
65
  rb_check_arity(argc, 0, 1); \
66
66
  \
67
- CONTENT_FROM_BLOCK; \
67
+ CONTENT_FROM_BLOCK \
68
68
  const char *tag = #element_name; \
69
69
  char *string = element(tag, strlen(tag), RSTRING_PTR(content), RSTRING_LEN(content), argv[0]); \
70
70
  VALUE rstring = rb_utf8_str_new_cstr(string); \
@@ -110,17 +110,24 @@ static VALUE external_sanitize(RB_UNUSED_VAR(VALUE self), VALUE string) {
110
110
  char *str = RSTRING_PTR(string);
111
111
 
112
112
  char dest[slen + 1];
113
- int index = 0;
114
- int open = 0;
115
- int opened = 0;
113
+
114
+ unsigned int index = 0;
115
+ unsigned int open = 0;
116
+ unsigned int modified = 0;
117
+ unsigned int entity = 0;
116
118
 
117
119
  for (unsigned int i = 0; i < slen; i++) {
118
120
  if (str[i] == '<') {
119
121
  open = 1;
120
- opened = 1;
122
+ modified = 1;
121
123
  } else if (str[i] == '>') {
122
124
  open = 0;
123
- } else if (!open) {
125
+ } else if (str[i] == '&') {
126
+ entity = 1;
127
+ modified = 1;
128
+ } else if (str[i] == ';') {
129
+ entity = 0;
130
+ } else if (!open && !entity) {
124
131
  dest[index++] = str[i];
125
132
  }
126
133
  }
@@ -128,10 +135,10 @@ static VALUE external_sanitize(RB_UNUSED_VAR(VALUE self), VALUE string) {
128
135
  dest[index] = '\0';
129
136
 
130
137
  /*
131
- * If a tag was never opened, return the original string, otherwise create a new
132
- * string from our destination buffer.
138
+ * If the string was never modified, return the original string, otherwise
139
+ * create a new string from our destination buffer.
133
140
  */
134
- if (opened) {
141
+ if (modified) {
135
142
  return rb_utf8_str_new_cstr(dest);
136
143
  } else {
137
144
  return string;
@@ -221,7 +228,7 @@ static char * hash_value_to_attribute(const char *attr, const size_t attrlen, VA
221
228
 
222
229
  Check_Type(value, T_HASH);
223
230
 
224
- if (rb_hash_size(value) == 1) {
231
+ if (RHASH_SIZE(value) == 0) {
225
232
  return strdup("");
226
233
  }
227
234
 
@@ -432,7 +439,7 @@ static VALUE external_to_attribute(RB_UNUSED_VAR(VALUE self), VALUE attr, VALUE
432
439
  static VALUE external_to_attributes(RB_UNUSED_VAR(VALUE self), VALUE attributes) {
433
440
  Check_Type(attributes, T_HASH);
434
441
 
435
- if (rb_hash_size(attributes) == 1) {
442
+ if (RHASH_SIZE(attributes) == 0) {
436
443
  return rb_utf8_str_new_cstr("");
437
444
  }
438
445
 
@@ -446,37 +453,32 @@ static VALUE external_to_attributes(RB_UNUSED_VAR(VALUE self), VALUE attributes)
446
453
  }
447
454
 
448
455
  static char * void_element(const char *tag, size_t tlen, VALUE attributes) {
449
- /* T_IMEMO is what we get if an optional argument was not passed. */
450
- if (TYPE(attributes) == T_IMEMO) {
451
- size_t total = tag_olen + tlen + tag_clen + 1;
452
- char *string = malloc(total);
453
- char *ptr;
454
- char *end = string + total;
456
+ const char *empty = "";
457
+ char *attrs = hash_value_to_attribute(empty, 0, attributes);
458
+ size_t alen = strlen(attrs);
455
459
 
456
- ptr = stecpy(string, tag_open, end);
457
- ptr = stecpy(ptr, tag, end);
458
- ptr = stecpy(ptr, tag_close, end);
460
+ size_t total = tag_olen + tlen + tag_clen + 1;
459
461
 
460
- return string;
461
- } else {
462
- const char *empty = "";
463
- char *attrs = hash_value_to_attribute(empty, 0, attributes);
462
+ /* If we have some attributes, add a space and the attributes' length. */
463
+ if (alen > 0) {
464
+ total += splen + alen;
465
+ }
466
+
467
+ char *dest = malloc(total);
468
+ char *ptr = NULL;
469
+ char *end = dest + total;
464
470
 
465
- size_t total = tag_olen + tlen + splen + strlen(attrs) + tag_clen + 1;
466
- char *string = malloc(total);
467
- char *ptr;
468
- char *end = string + total;
471
+ ptr = stecpy(dest, tag_open, end);
472
+ ptr = stecpy(ptr, tag, end);
469
473
 
470
- ptr = stecpy(string, tag_open, end);
471
- ptr = stecpy(ptr, tag, end);
474
+ if (alen > 0) {
472
475
  ptr = stecpy(ptr, space, end);
473
476
  ptr = stecpy(ptr, attrs, end);
474
- ptr = stecpy(ptr, tag_close, end);
477
+ }
475
478
 
476
- free(attrs);
479
+ ptr = stecpy(ptr, tag_close, end);
477
480
 
478
- return string;
479
- }
481
+ return dest;
480
482
  }
481
483
 
482
484
  /*
@@ -571,7 +573,7 @@ static VALUE external_element(int argc, VALUE *arguments, RB_UNUSED_VAR(VALUE se
571
573
 
572
574
  StringValue(tag);
573
575
 
574
- CONTENT_FROM_BLOCK;
576
+ CONTENT_FROM_BLOCK
575
577
 
576
578
  char *string = element(RSTRING_PTR(tag), RSTRING_LEN(tag), RSTRING_PTR(content), RSTRING_LEN(content), attributes);
577
579
  VALUE rstring = rb_utf8_str_new_cstr(string);
Binary file
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+ require 'berns'
3
+
4
+ module Berns
5
+ # An HTML builder DSL using Berns' HTML methods.
6
+ class Builder
7
+ def initialize(&block)
8
+ @block = block
9
+ @buffer = +''
10
+ end
11
+
12
+ # @return [String]
13
+ def call(*args, **opts)
14
+ instance_exec(*args, **opts, &@block)
15
+ to_s
16
+ end
17
+
18
+ # @return [String]
19
+ def to_s
20
+ @buffer.freeze
21
+ end
22
+
23
+ # Append text to the buffer.
24
+ #
25
+ # @param string [String]
26
+ # @return [String]
27
+ def text(string)
28
+ @buffer << Berns.escape_html(string.to_s)
29
+ end
30
+
31
+ # Append an arbitrary standard element to the buffer.
32
+ #
33
+ # @return [String]
34
+ def element(*args, **opts, &block)
35
+ content = Builder.new.instance_exec(*args, **opts, &block) if block
36
+ @buffer << Berns.element(*args, **opts) { content }
37
+ end
38
+
39
+ # Append an arbitrary void element to the buffer.
40
+ #
41
+ # @return [String]
42
+ def void(*args, **opts)
43
+ @buffer << Berns.void(*args, **opts)
44
+ end
45
+
46
+ Berns::STANDARD.each do |meth|
47
+ define_method(meth) do |*args, **opts, &block|
48
+ content = Builder.new.instance_exec(*args, **opts, &block) if block
49
+ @buffer << Berns.send(meth, *args, **opts) { content }
50
+ end
51
+ end
52
+
53
+ Berns::VOID.each do |meth|
54
+ define_method(meth) do |*args, **opts|
55
+ @buffer << Berns.send(meth, *args, **opts)
56
+ end
57
+ end
58
+ end
59
+ end
data/lib/berns/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Berns
3
- VERSION = '3.2.1'
3
+ VERSION = '3.5.0'
4
4
  end
data/lib/berns.rb CHANGED
@@ -1,3 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
  require 'berns/berns'
3
3
  require 'berns/version'
4
+
5
+ module Berns # :nodoc:
6
+ autoload :Builder, 'berns/builder'
7
+
8
+ STANDARD = %i[
9
+ a abbr address article aside audio b bdi bdo blockquote body button canvas
10
+ caption cite code colgroup datalist dd del details dfn dialog div dl dt em
11
+ fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 head header html i
12
+ iframe ins kbd label legend li main map mark menu meter nav noscript object
13
+ ol optgroup option output p picture pre progress q rp rt ruby s samp script
14
+ section select small span strong style sub summary table tbody td template
15
+ textarea tfoot th thead time title tr u ul var video
16
+ ].freeze
17
+
18
+ VOID = %i[
19
+ area base br col embed hr img input link menuitem meta param source track wbr
20
+ ].freeze
21
+
22
+ # @return [String]
23
+ def self.build(*args, **opts, &block)
24
+ Builder.new(&block).call(*args, **opts)
25
+ end
26
+ end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: berns
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.1
4
+ version: 3.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Taylor Beck
8
8
  - Evan Lecklider
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-06-15 00:00:00.000000000 Z
12
+ date: 2022-02-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: benchmark-ips
@@ -167,15 +167,17 @@ files:
167
167
  - ext/berns/hescape.c
168
168
  - ext/berns/hescape.h
169
169
  - lib/berns.rb
170
- - lib/berns/berns.so
170
+ - lib/berns/berns.bundle
171
+ - lib/berns/builder.rb
171
172
  - lib/berns/version.rb
172
173
  homepage: https://github.com/evanleck/berns
173
174
  licenses:
174
175
  - MIT
175
176
  metadata:
176
177
  bug_tracker_uri: https://github.com/evanleck/berns/issues
178
+ rubygems_mfa_required: 'true'
177
179
  source_code_uri: https://github.com/evanleck/berns
178
- post_install_message:
180
+ post_install_message:
179
181
  rdoc_options: []
180
182
  require_paths:
181
183
  - lib
@@ -188,10 +190,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
188
190
  requirements:
189
191
  - - ">="
190
192
  - !ruby/object:Gem::Version
191
- version: '0'
193
+ version: '2.0'
192
194
  requirements: []
193
- rubygems_version: 3.2.15
194
- signing_key:
195
+ rubygems_version: 3.3.3
196
+ signing_key:
195
197
  specification_version: 4
196
198
  summary: A utility library for generating HTML strings.
197
199
  test_files: []
data/lib/berns/berns.so DELETED
Binary file