serbea 0.6.0 β†’ 0.7.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: edd4caa76947988e564a917a9457014cfe194d53ec8b0edf1aae5479ea8b9cf4
4
- data.tar.gz: e93987c1eb193535cf47fc7dc57b412407d5ad6a832bc8ffd5966bc7bbc5c322
3
+ metadata.gz: 3c69f1aa7916899c197489290d683afad319c66bccff8bd32d3e9a0e943bfa41
4
+ data.tar.gz: 267760c9cbbc3cbde6af0a3479b905c4bd8eca6e601dee2d1bb43cb0a819aabd
5
5
  SHA512:
6
- metadata.gz: e59b9abc02d1f2a272bef3adf5239fb385bde5924ad4c3a2bb2f8efe6ec11caab6cf2a4b2901fc9ea59821e119b8ea7fb9dc700a4e22773b535633284da89afe
7
- data.tar.gz: 1bddcc5a82fa3cd62ceead72600ec621e6fcad08d2e3a6c58041cca972505917c19f89b2861514599feef93f9449c0eec78b7fbb69de97135fd2aefebcc4bab8
6
+ metadata.gz: c50177a140769d470f500446902009faac98bb390ef44f7930ed28b3c1cb92ac0a499e15ee2c66384ef0c7a52e55dca892ff2c17fe3f85bc8022b92c2f1c6a6a
7
+ data.tar.gz: 330296668991421be44b92b1a4a87470dc93ffd78e4b00530e26003fff1af293a9b4d51ee46fbcac2b541379a6e4c2a0179ec6365fafce14c219905ee8b73617
data/Gemfile CHANGED
@@ -4,3 +4,5 @@ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
5
  # Specify your gem's dependencies in serbea.gemspec
6
6
  gemspec
7
+
8
+ gem "erubi", github: "jaredcwhite/erubi", branch: "config-literal-prefix"
data/README.md CHANGED
@@ -1,3 +1,109 @@
1
1
  πŸ‘‘ Serbea: Similar to ERB, Except Awesomer
2
2
 
3
- The Ruby template language you always dreamed of is here. Le roi est mort, vive le roi!
3
+ The Ruby template language you always dreamed of is 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 application, 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.libs << "test"
6
- t.libs << "lib"
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
@@ -9,7 +9,13 @@ require "serbea/component_renderer"
9
9
  module Tilt
10
10
  class SerbeaTemplate < ErubiTemplate
11
11
  def prepare
12
- @options.merge!(outvar: "@_erbout", bufval: "Serbea::Buffer.new", engine_class: Serbea::TemplateEngine)
12
+ @options.merge!(
13
+ outvar: "@_erbout",
14
+ bufval: "Serbea::Buffer.new",
15
+ literal_prefix: "{%",
16
+ literal_postfix: "%}",
17
+ engine_class: Serbea::TemplateEngine
18
+ )
13
19
  super
14
20
  end
15
21
 
@@ -1,4 +1,5 @@
1
1
  require "serbea/rouge_lexer"
2
+ require "bridgetown-core"
2
3
 
3
4
  module Bridgetown
4
5
  class SerbeaView < RubyTemplateView
@@ -70,3 +71,13 @@ module Bridgetown
70
71
  end
71
72
  end
72
73
  end
74
+
75
+ Bridgetown::Hooks.register :site, :pre_render, reloadable: false 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
@@ -29,9 +29,27 @@ module Serbea
29
29
  Pipeline.new(context, value)
30
30
  end
31
31
 
32
- def helper(name, &block)
33
- self.class.send(:define_method, name, &block)
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)
@@ -1,3 +1,5 @@
1
+ require "strscan"
2
+
1
3
  module Serbea
2
4
  class Buffer < String
3
5
  def concat_to_s(input)
@@ -87,22 +89,57 @@ module Serbea
87
89
  buff << text
88
90
  if code.length > 0
89
91
  original_line_length = code.lines.size
90
- processed_filters = false
91
-
92
- code = code.gsub('\|', "__PIPE_C__")
93
-
94
- subs = code.gsub(/\s*\|>?\s+(.*?)\s([^|}]*)/) do
95
- args = $2
96
- args = nil if args.strip == ""
97
- prefix = processed_filters ? ")" : "))"
98
- processed_filters = true
99
- "#{prefix}.filter(:#{$1.chomp(":")}" + (args ? ", #{args}" : "")
92
+
93
+ s = StringScanner.new(code[2...-2])
94
+ done = false
95
+ escaped_segment = ""
96
+ segments = []
97
+ while !done
98
+ portion = s.scan_until(/\|>?/)
99
+ if portion
100
+ if portion.end_with?('\|')
101
+ # the pipe is escaped, so save that for later
102
+ escaped_segment += portion.sub(/\\\|$/, "|")
103
+ elsif escaped_segment.length > 0
104
+ # we already have escaped content, so finish that up
105
+ segments << escaped_segment + portion.sub(/\|>?$/, "")
106
+ escaped_segment = ""
107
+ else
108
+ # let's find out if this is actionable now
109
+ if s.check(/\|/)
110
+ # nope, the next character is another pipe, so let's escape
111
+ s.pos += 1
112
+ escaped_segment += portion + "|"
113
+ else
114
+ # finally, we have liftoff!
115
+ segments << portion.sub(/\|>?$/, "")
116
+ end
117
+ end
118
+ else
119
+ # we've reached the last bit of the code
120
+ if escaped_segment.length > 0
121
+ # escape and get the rest
122
+ segments << escaped_segment + s.rest
123
+ else
124
+ # or just the rest will do
125
+ segments << s.rest
126
+ end
127
+ done = true
128
+ end
129
+ end
130
+
131
+ segments[0] = "pipeline(self, (#{segments[0].strip}))"
132
+ segments[1..-1].each_with_index do |segment, index|
133
+ filter, args = segment.strip.match(/([^ :]*)(.*)/m).captures
134
+ segments[index + 1] = ".filter(:" + filter
135
+ if args == ""
136
+ segments[index + 1] += ")"
137
+ else
138
+ segments[index + 1] += "," + args.sub(/^:/, "") + ")"
139
+ end
100
140
  end
101
-
102
- pipeline_suffix = processed_filters ? ") %}" : ")) %}"
103
-
104
- subs = subs.sub("{{", "{%= pipeline(self, (").sub("}}", pipeline_suffix).gsub("__PIPE_C__", '|')
105
141
 
142
+ subs = "{%= #{segments.join} %}"
106
143
  buff << subs
107
144
 
108
145
  (original_line_length - subs.lines.size).times do
@@ -127,8 +164,8 @@ module Serbea
127
164
 
128
165
  buff << text
129
166
  if code.length > 0
130
- code.sub! /^\{%@/, ""
131
- code.sub! /%}$/, ""
167
+ code.sub!(/^\{%@/, "")
168
+ code.sub!(/%}$/, "")
132
169
  unless ["end", ""].include? code.strip
133
170
  original_line_length = code.lines.size
134
171
 
@@ -1,3 +1,3 @@
1
1
  module Serbea
2
- VERSION = "0.6.0"
2
+ VERSION = "0.7.0"
3
3
  end
@@ -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.6.0
4
+ version: 0.7.0
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-10-27 00:00:00.000000000 Z
11
+ date: 2020-10-31 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