Replace the use of StringValue
with Check_Type
, which more accurately
reflects what it's used for.
Version 4.3.1 adds the -march=native
and -mtune=native
C flags and enables
them by default. These can be disabled with --disable-march-tune-native
when
installing the gem.
Version 4.3.0 contains a major refactor of string allocation functions, introducing a series of functions for handling strings in Berns and pushing memory allocation to Ruby by using Ruby's ruby_xmalloc, ruby_xfree, and ruby_xrealloc.
Allow inlining empty_value_to_attribute
.
Fix a memory leak in void_element
.
Rightsize the allocated memory from hash_value_to_attribute
before returning.
Instances of the Berns::Builder
class can now be #call
'ed multiple times,
letting instances of Berns::Builder
act like template objects.
No code changes were made, but the required Ruby version has been increased to
= 2.7.0 in keeping with 2.6's recent end-of-life.
Add the #element
and #void
methods to blocks evaluated by Berns::Builder
.
Add the Berns::Builder
class, enabling simpler template composition.
This class effectively works like a DSL for HTML template composition. For
example, to construct an article
with a heading and paragraph in earlier
versions you might do something like this:
markup = Berns.article do
content = Berns.h1 { 'Heading' }
content + Berns.p { 'A paragraph.' }
end
And the same thing using the new Berns::Builder
class:
template = Berns::Builder.new do
article {
h1 { 'Heading' }
p { 'A paragraph.' }
}
end
markup = template.call # => "<article><h1>Heading</h1><p>A paragraph.</p></article>"
This has two advantages over the previous approach:
- There's no need to build the string as you go. Any
Berns
standard or void element method will automatically append to an internal buffer. - There's less repetition since you don't need to add
Berns
in front of each element method call.
This new class is heavily inspired by the likes of Papercraft, Markaby, and Arbre.
Remove the std=c99
flag.
Berns.sanitize
now removes HTML entities like <
as well as their
unescaped equivalents.
Tweak Berns.sanitize
to return the original string if no modifications were
made.
Before:
clean 8.452M (± 1.8%) i/s - 42.530M in 5.033708s
dirty 7.411M (± 1.1%) i/s - 37.561M in 5.069121s
After:
clean 15.509M (± 1.4%) i/s - 78.419M in 5.057355s
dirty 7.515M (± 1.1%) i/s - 37.948M in 5.050435s
Rewrite Berns.sanitize
in C. It's still incredibly naive and, as noted in the
README, should probably only be used with trusted strings.
Before:
clean 3.404M (± 0.8%) i/s - 17.251M in 5.069001s
dirty 690.392k (± 1.7%) i/s - 3.506M in 5.079129s
After:
clean 8.744M (± 1.1%) i/s - 43.800M in 5.009626s
dirty 7.473M (± 0.7%) i/s - 37.734M in 5.049443s
- Refactor the way
$CFLAGS
are added to use theappend_cflags
method provided bymkmf
. - Add a few more flags including
-flto
. - Remove the unneeded
create_header
.
Several minor fixes including:
- Append to
$CFLAGS
instead of overwriting it. - Set
-std=c99
- Change optimization level to 2 from 3.
- Fix a case of variable shadowing.
Stop passing pointers to Ruby's Hash values. I'm not sure that this was ever a good idea.
Replace calls to calloc
with strdup("")
, which seems more correct.
Fix two potential off-by-one errors when calculating string length.
Fix an error when hash arguments to to_attribute
have a false
value. Prior
to this release the value would be converted to a string but with this release
the attribute is dropped altogether, which was the behavior before v3.0.
Version 3.1.0 is a major refactor of the underlying C code introduced with version 3.0.0 that benefits from everything learned in the initial implementation.
The API has been split between internal and external functions. Internal functions accept a mix of C types and Ruby object types but tend to return C types. These are composed together into external functions that accept Ruby objects as arguments and return Ruby strings.
In addition, HTML escaping is now powered by the excellent k0kubun/hescape library written by Takashi Kokubun.
Convert non-string blocks of content into strings. This allows, for example, passing numeric objects as blocks of content to standard element methods without first having to convert them to strings.
Fix a regression when content blocks are false. They should be treated the same as if they are not there instead of throwing an error.
This allows the use of conditions in combination with content blocks e.g.
Berns.div { user_can_view? && "You can see this sometimes" }
Fix an ArgumentError
when passing a nested empty hash to to_attribute
.
Fix a buffer overflow error.
Ensure all returned strings are UTF-8 encoded.
Fix a regression when content blocks are nil. They should be treated the same as if they are not there instead of throwing an error.
Version 3.0 is another mostly API-compatible refactor of Berns, this time in blazing fast C! I debated simply calling this version 2.1.0 but because it's a complete rewrite it didn't seem right to do a simple point release and there may be corner cases that I've not accounted for in this new C-backed version.
Running the same benchmarks as from 2.0 but pitting 2.0 against 3.0 yields some
great speed improvements, particularly for the empty
and simple
cases.
These benchmarks were performed on a desktop with a AMD Ryzen 5 3600X 6-Core Processor running Linux Mint 20.1 and kernel 5.4.
Before:
empty 1.668M (± 0.6%) i/s - 8.356M in 5.011099s
simple 442.102k (± 1.3%) i/s - 2.214M in 5.008068s
nested 267.716k (± 0.4%) i/s - 1.357M in 5.068747s
After:
empty 3.573M (± 1.2%) i/s - 17.881M in 5.005001s
simple 840.631k (± 0.6%) i/s - 4.253M in 5.059771s
nested 267.281k (± 0.5%) i/s - 1.347M in 5.037887s
With both empty and simple attributes we see performance effectively double, and with nested attributes performance remains more or less the same.
This is another set of fairly contrived benchmarks, testing a singleton method,
void
call, and element
call against each other.
Before:
br 3.061M (± 0.8%) i/s - 15.613M in 5.100154s
void("br") 6.141M (± 1.4%) i/s - 30.990M in 5.047338s
element("div") 2.789M (± 0.6%) i/s - 14.171M in 5.080626s
After:
br 8.155M (± 1.0%) i/s - 41.339M in 5.069681s
void("br") 9.782M (± 1.5%) i/s - 49.096M in 5.020114s
element("div") 6.769M (± 1.1%) i/s - 33.983M in 5.021362s
Lastly, benchmarking to_attributes
with the following hash as the only
argument shows about double the performance with 3.0.
ATTRS = { this: 'tag', should: 'work', data: { foo: 'bar', bar: { baz: 'foo' } } }.freeze
Before:
to_attributes 228.829k (± 1.3%) i/s - 1.159M in 5.065714s
After:
to_attributes 457.387k (± 1.2%) i/s - 2.305M in 5.041036s
Version 2.0 is a mostly API-compatible refactor of all of the core methods that make up Berns. The goal is to improve performance, mostly using mutable strings and inlining variables that were otherwise short lived.
In addition, the target Ruby version has been raised to 2.5 or later. 2.4 has reached its end of life.
Running this benchmarking code:
Benchmark.ips do |x|
x.report('empty') { Berns.element(:a) { 'Link to something' } }
x.report('simple') { Berns.element(:a, { href: 'Something', class: 'my-class' }) { 'Link to something' } }
x.report('nested') { Berns.element(:a, { href: 'Something', class: 'my-class', data: { something: 'Else' } }) { 'Link to something' } }
x.compare!
end
Before:
empty 993.521k (± 1.7%) i/s - 5.062M in 5.096368s
simple 340.795k (± 0.4%) i/s - 1.729M in 5.074101s
nested 215.160k (± 1.0%) i/s - 1.081M in 5.025324s
After:
empty 1.769M (± 1.9%) i/s - 9.012M in 5.094973s
simple 441.020k (± 1.0%) i/s - 2.233M in 5.063326s
nested 280.255k (± 3.0%) i/s - 1.400M in 5.001009s
With empty attributes we see ~ 100% increase in iterations per second, with simple attributes we see ~ 30% increase in the same, and with nested attributes we see ~ 30% increase as well.
With version 1.3, nested HTML attributes can be created with nil keys and
boolean values to produce e.g. "data-foo data-foo-bar='whatever'" from data: { foo: { nil => true, bar: 'whatever' } }
Starting with version 1.2, Berns will now HTML-escape all attribute values using
CGI.escapeHTML
. This should prevent attribute values from escaping themselves
and injecting HTML into the DOM.
- Add
#sanitize
method.