serbea 0.6.2 β 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -0
- data/README.md +108 -2
- data/Rakefile +5 -3
- data/lib/serbea.rb +7 -2
- data/lib/serbea/bridgetown_support.rb +11 -18
- data/lib/serbea/helpers.rb +21 -3
- data/lib/serbea/rails_support.rb +0 -1
- data/lib/serbea/template_engine.rb +56 -31
- data/lib/version.rb +1 -1
- data/serbea.gemspec +1 -0
- metadata +16 -3
- data/lib/serbea/component_renderer.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 002caf1e91a373f0b94de2cfca37145b80d725205296623276e0693aed768953
|
4
|
+
data.tar.gz: 0b27aa02debeb406fc4896fb497367accbe19250f9fb5d5f022ff256af538da5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 366027ed40d71bc2279c26c11f80dda3dae1345bc14324668f591e8e2a7eece974fc4ab9947c23bc31bbd4a68c4187c17b9761066454d79008626191778083b4
|
7
|
+
data.tar.gz: 537d54b0be5690ad547acf848648061056499fe530bdc434a73e55672202d3f0ae54118a4e4cb92a03aba884a8dfd14a91b447533c708633dfb67a4229d11946
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,3 +1,109 @@
|
|
1
|
-
π Serbea: Similar to ERB, Except Awesomer
|
1
|
+
# π Serbea: Similar to ERB, Except Awesomer
|
2
2
|
|
3
|
-
The Ruby template language you always dreamed of is here.
|
3
|
+
The Ruby template language you've always dreamed of is finally here. _Le roi est mort, vive le roi!_
|
4
|
+
|
5
|
+
Serbea combines the best ideas from "brace-style" template languages such as Liquid, Nunjucks, Twig, Jinja, Mustache, etc.βand applies them to the world of ERB. You can use Serbea in Rails applications, Bridgetown static sites, or pretty much any Ruby scenario you could imagine.
|
6
|
+
|
7
|
+
## Features
|
8
|
+
|
9
|
+
* Real Ruby. Like, for real.
|
10
|
+
* Filters!!! Pipeline operators!!!
|
11
|
+
* Autoescaped variables by default within the pipeline (`{{ }}`) tags. Use the `safe`/`raw` or `escape`/`h` filters to control escaping on output.
|
12
|
+
* Render directive `{%@ %}` as shortcut for rendering either string-named partials (`render "tmpl"`) or object instances (`render MyComponent.new`).
|
13
|
+
* Supports every convention of ERB and builds upon it with new features (which is why it's "awesomer!").
|
14
|
+
* Builtin frontmatter support (even in Rails!) so you can access the variables written into the top YAML within your templates.
|
15
|
+
* The filters accessible within Serbea templates are either helpers (where the variable gets passed as the first argument) or instance methods of the variable itself, so you can build extremely expressive pipelines that take advantage of the code you already know and love. (For example, in Rails you could write `{{ "My Link" | link_to: route_path }}`).
|
16
|
+
* The `Serbea::Pipeline.exec` method lets you pass a pipeline template in, along with an optional input value or included helpers module, and you'll get the output as a object of any type (not converted to a string like in traditional templates). For example: `Serbea::Pipeline.exec("[1,2,3] |> map: ->(i) { i * 10 }")` will return `[10, 20, 30]`.
|
17
|
+
|
18
|
+
## What It Looks Like
|
19
|
+
|
20
|
+
```hbs
|
21
|
+
# example.serb
|
22
|
+
|
23
|
+
{% wow = capture do %}
|
24
|
+
This is {{ "amazing" + "!" | upcase }}
|
25
|
+
{% end.each_char.reduce("") do |newstr, c|
|
26
|
+
newstr += " #{c}"
|
27
|
+
end.strip %}
|
28
|
+
|
29
|
+
{{ wow | prepend: "OMG! " }}
|
30
|
+
```
|
31
|
+
|
32
|
+
```hbs
|
33
|
+
<p>
|
34
|
+
{%
|
35
|
+
helper :multiply_array do |input, multiply_by = 2|
|
36
|
+
input.map do |i|
|
37
|
+
i.to_i * multiply_by
|
38
|
+
end
|
39
|
+
end
|
40
|
+
%}
|
41
|
+
|
42
|
+
Multiply! {{ [1,3,6, "9"] | multiply_array: 10 }}
|
43
|
+
</p>
|
44
|
+
```
|
45
|
+
|
46
|
+
```hbs
|
47
|
+
{%= form classname: "checkout" do |f| %}
|
48
|
+
{{ f.input :first_name, required: true | errors: error_messages }}
|
49
|
+
{% end %}
|
50
|
+
```
|
51
|
+
|
52
|
+
```hbs
|
53
|
+
{%= render "box" do %}
|
54
|
+
This is **dope!**
|
55
|
+
{%= render "card", title: "Nifty!" do %}
|
56
|
+
So great.
|
57
|
+
{% end %}
|
58
|
+
{% end %}
|
59
|
+
|
60
|
+
# Let's simplify that using the new render directive!
|
61
|
+
|
62
|
+
{%@ "box" do %}
|
63
|
+
This is **dope!**
|
64
|
+
{%@ "card", title: "Nifty!" do %}
|
65
|
+
So great.
|
66
|
+
{% end %}
|
67
|
+
{% end %}
|
68
|
+
```
|
69
|
+
|
70
|
+
```hbs
|
71
|
+
# Works with ViewComponent!
|
72
|
+
|
73
|
+
{%= render(Theme::DropdownComponent.new(name: "banner", label: "Banners")) do |dropdown| %}
|
74
|
+
{% RegistryTheme::BANNERS.each do |banner| %}
|
75
|
+
{% dropdown.slot(:item, value: banner) do %}
|
76
|
+
<img src="{{ banner | parameterize: separator: "_" | prepend: "/themes/" | append: ".jpg" }}">
|
77
|
+
<strong>{{ banner }}</strong>
|
78
|
+
{% end %}
|
79
|
+
{% end %}
|
80
|
+
{% end %}
|
81
|
+
|
82
|
+
# Even better, use the new render directive!
|
83
|
+
|
84
|
+
{%@ Theme::DropdownComponent name: "banner", label: "Banners" do |dropdown| %}
|
85
|
+
{% RegistryTheme::BANNERS.each do |banner| %}
|
86
|
+
{% dropdown.slot(:item, value: banner) do %}
|
87
|
+
<img src="{{ banner | parameterize: separator: "_" | prepend: "/themes/" | append: ".jpg" }}">
|
88
|
+
<strong>{{ banner }}</strong>
|
89
|
+
{% end %}
|
90
|
+
{% end %}
|
91
|
+
{% end %}
|
92
|
+
```
|
93
|
+
|
94
|
+
```hbs
|
95
|
+
# The | and |> pipeline operators are equivalent, so you can write like this if you want!
|
96
|
+
|
97
|
+
{{
|
98
|
+
[1,2,3] |>
|
99
|
+
map: -> i {
|
100
|
+
i * 10
|
101
|
+
} |>
|
102
|
+
filter: -> i do
|
103
|
+
i > 15
|
104
|
+
end |>
|
105
|
+
assign_to: :array_length
|
106
|
+
}}
|
107
|
+
|
108
|
+
Array length: {{ @array_length.length }}
|
109
|
+
```
|
data/Rakefile
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
require "rake/testtask"
|
3
|
+
require "bundler"
|
4
|
+
|
5
|
+
Bundler.setup
|
3
6
|
|
4
7
|
Rake::TestTask.new(:test) do |t|
|
5
|
-
t.
|
6
|
-
t.
|
7
|
-
t.test_files = FileList["test/**/test_*.rb"]
|
8
|
+
t.test_files = FileList["test/test.rb"]
|
9
|
+
t.warning = false
|
8
10
|
end
|
9
11
|
|
10
12
|
task :default => :test
|
data/lib/serbea.rb
CHANGED
@@ -4,12 +4,17 @@ require "tilt/erubi"
|
|
4
4
|
require "serbea/helpers"
|
5
5
|
require "serbea/pipeline"
|
6
6
|
require "serbea/template_engine"
|
7
|
-
require "serbea/component_renderer"
|
8
7
|
|
9
8
|
module Tilt
|
10
9
|
class SerbeaTemplate < ErubiTemplate
|
11
10
|
def prepare
|
12
|
-
@options.merge!(
|
11
|
+
@options.merge!(
|
12
|
+
outvar: "@_erbout",
|
13
|
+
bufval: "Serbea::Buffer.new",
|
14
|
+
literal_prefix: "{%",
|
15
|
+
literal_postfix: "%}",
|
16
|
+
engine_class: Serbea::TemplateEngine
|
17
|
+
)
|
13
18
|
super
|
14
19
|
end
|
15
20
|
|
@@ -40,6 +40,8 @@ module Bridgetown
|
|
40
40
|
#
|
41
41
|
# @return [String] The converted content.
|
42
42
|
def convert(content, convertible)
|
43
|
+
return content if convertible.data[:template_engine] != "serbea"
|
44
|
+
|
43
45
|
serb_view = Bridgetown::SerbeaView.new(convertible)
|
44
46
|
|
45
47
|
serb_renderer = Tilt::SerbeaTemplate.new(convertible.relative_path) { content }
|
@@ -53,16 +55,17 @@ module Bridgetown
|
|
53
55
|
end
|
54
56
|
end
|
55
57
|
|
56
|
-
def matches(ext, convertible
|
57
|
-
if convertible
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
end
|
58
|
+
def matches(ext, convertible)
|
59
|
+
if convertible.data[:template_engine] == "serbea" ||
|
60
|
+
(convertible.data[:template_engine].nil? &&
|
61
|
+
@config[:template_engine] == "serbea")
|
62
|
+
convertible.data[:template_engine] = "serbea"
|
63
|
+
return true
|
63
64
|
end
|
64
65
|
|
65
|
-
super(ext)
|
66
|
+
super(ext).tap do |ext_matches|
|
67
|
+
convertible.data[:template_engine] = "serbea" if ext_matches
|
68
|
+
end
|
66
69
|
end
|
67
70
|
|
68
71
|
def output_ext(ext)
|
@@ -71,13 +74,3 @@ module Bridgetown
|
|
71
74
|
end
|
72
75
|
end
|
73
76
|
end
|
74
|
-
|
75
|
-
Bridgetown::Hooks.register :site, :pre_render do |site|
|
76
|
-
# make sure Liquid doesn't find {% %} and decide to process Serbea code!
|
77
|
-
site.contents.each do |convertible|
|
78
|
-
convertible.data.render_with_liquid = false if convertible.extname == ".serb"
|
79
|
-
end
|
80
|
-
site.layouts.values.each do |convertible|
|
81
|
-
convertible.data.render_with_liquid = false if convertible.ext == ".serb"
|
82
|
-
end
|
83
|
-
end
|
data/lib/serbea/helpers.rb
CHANGED
@@ -22,16 +22,34 @@ module Serbea
|
|
22
22
|
@_erbout = previous_buffer_state
|
23
23
|
@output_buffer = previous_ob_state
|
24
24
|
|
25
|
-
result
|
25
|
+
result&.html_safe
|
26
26
|
end
|
27
27
|
|
28
28
|
def pipeline(context, value)
|
29
29
|
Pipeline.new(context, value)
|
30
30
|
end
|
31
31
|
|
32
|
-
def helper(name, &
|
33
|
-
self.class.
|
32
|
+
def helper(name, &helper_block)
|
33
|
+
self.class.define_method(name) do |*args, &block|
|
34
|
+
previous_buffer_state = @_erbout
|
35
|
+
@_erbout = Serbea::Buffer.new
|
36
|
+
|
37
|
+
# For compatibility with ActionView, not used by Bridgetown normally
|
38
|
+
previous_ob_state = @output_buffer
|
39
|
+
@output_buffer = Serbea::Buffer.new
|
40
|
+
|
41
|
+
result = helper_block.call(*args, &block)
|
42
|
+
if @output_buffer != ""
|
43
|
+
# use Rails' ActionView buffer if present
|
44
|
+
result = @output_buffer
|
45
|
+
end
|
46
|
+
@_erbout = previous_buffer_state
|
47
|
+
@output_buffer = previous_ob_state
|
48
|
+
|
49
|
+
result.is_a?(String) ? result.html_safe : result
|
50
|
+
end
|
34
51
|
end
|
52
|
+
alias_method :macro, :helper
|
35
53
|
|
36
54
|
def h(input)
|
37
55
|
ERB::Util.h(input.to_s)
|
data/lib/serbea/rails_support.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "strscan"
|
2
|
+
|
1
3
|
module Serbea
|
2
4
|
class Buffer < String
|
3
5
|
def concat_to_s(input)
|
@@ -64,13 +66,11 @@ module Serbea
|
|
64
66
|
|
65
67
|
# Ensure the raw "tag" will strip out all ERB-style processing
|
66
68
|
until string.empty?
|
67
|
-
text, code, string = string.partition(/{% raw %}.*?{% endraw %}/m)
|
69
|
+
text, code, string = string.partition(/{% raw %}(.*?){% endraw %}/m)
|
68
70
|
|
69
71
|
buff << text
|
70
72
|
if code.length > 0
|
71
|
-
buff <<
|
72
|
-
sub("{% raw %}", "").
|
73
|
-
sub("{% endraw %}", "").
|
73
|
+
buff << $1.
|
74
74
|
gsub("{{", "__RAW_START_PRINT__").
|
75
75
|
gsub("}}", "__RAW_END_PRINT__").
|
76
76
|
gsub("{%", "__RAW_START_EVAL__").
|
@@ -82,27 +82,61 @@ module Serbea
|
|
82
82
|
string = buff
|
83
83
|
buff = ""
|
84
84
|
until string.empty?
|
85
|
-
text, code, string = string.partition(/{{.*?}}/m)
|
85
|
+
text, code, string = string.partition(/{{(.*?)}}/m)
|
86
86
|
|
87
87
|
buff << text
|
88
88
|
if code.length > 0
|
89
89
|
original_line_length = code.lines.size
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
90
|
+
|
91
|
+
s = StringScanner.new($1)
|
92
|
+
escaped_segment = ""
|
93
|
+
segments = []
|
94
|
+
until s.eos?
|
95
|
+
portion = s.scan_until(/\|>?/)
|
96
|
+
if portion
|
97
|
+
if portion.end_with?('\|')
|
98
|
+
# the pipe is escaped, so save that for later
|
99
|
+
escaped_segment += portion.sub(/\\\|$/, "|")
|
100
|
+
elsif escaped_segment.length > 0
|
101
|
+
# we already have escaped content, so finish that up
|
102
|
+
segments << escaped_segment + portion.sub(/\|>?$/, "")
|
103
|
+
escaped_segment = ""
|
104
|
+
else
|
105
|
+
# let's find out if this is actionable now
|
106
|
+
if s.check(/\|/)
|
107
|
+
# nope, the next character is another pipe, so let's escape
|
108
|
+
s.pos += 1
|
109
|
+
escaped_segment += portion + "|"
|
110
|
+
else
|
111
|
+
# finally, we have liftoff!
|
112
|
+
segments << portion.sub(/\|>?$/, "")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
else
|
116
|
+
# we've reached the last bit of the code
|
117
|
+
if escaped_segment.length > 0
|
118
|
+
# escape and get the rest
|
119
|
+
segments << escaped_segment + s.rest
|
120
|
+
else
|
121
|
+
# or just the rest will do
|
122
|
+
segments << s.rest
|
123
|
+
end
|
124
|
+
s.terminate
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
segments[0] = "pipeline(self, (#{segments[0].strip}))"
|
129
|
+
segments[1..-1].each_with_index do |segment, index|
|
130
|
+
filter, args = segment.strip.match(/([^ :]*)(.*)/m).captures
|
131
|
+
segments[index + 1] = ".filter(:" + filter
|
132
|
+
if args == ""
|
133
|
+
segments[index + 1] += ")"
|
134
|
+
else
|
135
|
+
segments[index + 1] += "," + args.sub(/^:/, "") + ")"
|
136
|
+
end
|
100
137
|
end
|
101
|
-
|
102
|
-
pipeline_suffix = processed_filters ? ") %}" : ")) %}"
|
103
|
-
|
104
|
-
subs = subs.sub("{{", "{%= pipeline(self, (").sub("}}", pipeline_suffix).gsub("__PIPE_C__", '|')
|
105
138
|
|
139
|
+
subs = "{%= #{segments.join} %}"
|
106
140
|
buff << subs
|
107
141
|
|
108
142
|
(original_line_length - subs.lines.size).times do
|
@@ -111,24 +145,15 @@ module Serbea
|
|
111
145
|
end
|
112
146
|
end
|
113
147
|
|
114
|
-
# Process any directives
|
115
|
-
#
|
116
|
-
# TODO: allow custom directives! aka
|
117
|
-
# {%@something whatever %}
|
118
|
-
# {%@script
|
119
|
-
# const foo = "really? #{really}!"
|
120
|
-
# alert(foo.toLowerCase())
|
121
|
-
# %}
|
122
|
-
# {%@preact AwesomeChart data={#{ruby_data.to_json}} %}
|
148
|
+
# Process any render directives
|
123
149
|
string = buff
|
124
150
|
buff = ""
|
125
151
|
until string.empty?
|
126
|
-
text, code, string = string.partition(/{
|
152
|
+
text, code, string = string.partition(/{%@(.*?)%}/m)
|
127
153
|
|
128
154
|
buff << text
|
129
155
|
if code.length > 0
|
130
|
-
code
|
131
|
-
code.sub! /%}$/, ""
|
156
|
+
code = $1
|
132
157
|
unless ["end", ""].include? code.strip
|
133
158
|
original_line_length = code.lines.size
|
134
159
|
|
data/lib/version.rb
CHANGED
data/serbea.gemspec
CHANGED
@@ -16,6 +16,7 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r!^(test|script|spec|features)/!) }
|
17
17
|
spec.require_paths = ["lib"]
|
18
18
|
|
19
|
+
spec.add_runtime_dependency("rake", "~> 13.0")
|
19
20
|
spec.add_runtime_dependency("erubi", "~> 1.9")
|
20
21
|
spec.add_runtime_dependency("activesupport", "~> 6.0")
|
21
22
|
spec.add_runtime_dependency("tilt", "~> 2.0")
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: serbea
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bridgetown Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-11-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '13.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '13.0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: erubi
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -64,7 +78,6 @@ files:
|
|
64
78
|
- Rakefile
|
65
79
|
- lib/serbea.rb
|
66
80
|
- lib/serbea/bridgetown_support.rb
|
67
|
-
- lib/serbea/component_renderer.rb
|
68
81
|
- lib/serbea/helpers.rb
|
69
82
|
- lib/serbea/pipeline.rb
|
70
83
|
- lib/serbea/rails_support.rb
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module Serbea
|
2
|
-
class ComponentRenderer
|
3
|
-
include Helpers
|
4
|
-
|
5
|
-
def initialize(variables = {})
|
6
|
-
@variables = variables
|
7
|
-
end
|
8
|
-
|
9
|
-
def respond_to_missing?(key, include_private = false)
|
10
|
-
@variables.key?(key)
|
11
|
-
end
|
12
|
-
|
13
|
-
def method_missing(key)
|
14
|
-
return @variables[key] if respond_to_missing?(key)
|
15
|
-
|
16
|
-
super
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|