serbea 0.8.0 → 0.10.2

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: 72bb8c66c7a574ed69bbec8995cd8a5e28fa72caf82768264b95b602ce1db815
4
- data.tar.gz: 3cece4d891ef8100bf0a5eb51570f89bdc58566914fe6f0b514d21a3a76e2f7e
3
+ metadata.gz: 18cedf3b548b6461f0d5c65ed89aafb009691248811fb1155fa59d4b1b28ccc9
4
+ data.tar.gz: 5f3300902b294bbbc720d6f4c550d43989a13906c22f93a232f8acba051acf2c
5
5
  SHA512:
6
- metadata.gz: 3b8d9d2677748d88ec34dbea4abbf65fb05253c0ccd8d1f4bcde2f9781ef9056194a6c25bfe8027dc20f906055fd7db023587aedbb944025109cc443ad9e720a
7
- data.tar.gz: a9622d5f4a502291fd8472eb24f2f6e479f434897eec51095eb2ba668b95fd1dd438eb400274d9811eaddfbd485e523d76ce7230245f9e8d11a284170afe3725
6
+ metadata.gz: 86fa6e781ab8d0b32bb4033dd33cae67167e97b43a14823f660a70d01532f36c041e6ef16861f3c8a1ff4aa0b3550651110455f110986588bd81e8aa3e84833f
7
+ data.tar.gz: ab43538b4eba0769421eb9138581b18a4d6c846dab8898c4bb037c1deba1c5e4dd6ee86a77fd99c9fdff597b41ff6330500d3cf190e93fc8e628ba10a1e253e7
data/Gemfile CHANGED
@@ -4,5 +4,3 @@ 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
@@ -13,7 +13,7 @@ Serbea combines the best ideas from "brace-style" template languages such as Liq
13
13
  * Supports every convention of ERB and builds upon it with new features (which is why it's "awesomer!").
14
14
  * Builtin frontmatter support so you can access the variables written into the top YAML within your templates. In any Rails view, including layouts, you'll have access to the `@frontmatter` ivar which is a merged `HashWithDotAccess::Hash` with data from any part of the view tree (partials, pages, layout).
15
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]`.
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("arr |> map: ->(i) { i * 10 }", arr: [1,2,3])` will return `[10, 20, 30]`.
17
17
 
18
18
  ## What It Looks Like
19
19
 
@@ -10,7 +10,7 @@ module Tilt
10
10
  def prepare
11
11
  @options.merge!(
12
12
  outvar: "@_erbout",
13
- bufval: "Serbea::Buffer.new",
13
+ bufval: "Serbea::OutputBuffer.new",
14
14
  literal_prefix: "{%",
15
15
  literal_postfix: "%}",
16
16
  engine_class: Serbea::TemplateEngine
@@ -32,9 +32,7 @@ if defined?(Rails::Railtie)
32
32
  ActiveSupport.on_load(:action_view) do
33
33
  require "serbea/rails_support"
34
34
 
35
- app.config.after_initialize do
36
- Serbea::Plugin.initialize_frontmatter
37
- end
35
+ ActionController::Base.include Serbea::Rails::FrontmatterControllerActions
38
36
  end
39
37
  end
40
38
  end
@@ -2,8 +2,10 @@ require "serbea/rouge_lexer"
2
2
  require "bridgetown-core"
3
3
 
4
4
  module Bridgetown
5
- class SerbeaView < RubyTemplateView
5
+ class SerbeaView < ERBView
6
+ alias_method :_erb_capture, :capture
6
7
  include Serbea::Helpers
8
+ alias_method :capture, :_erb_capture
7
9
 
8
10
  def partial(partial_name, options = {}, &block)
9
11
  options.merge!(options[:locals]) if options[:locals]
@@ -17,14 +19,6 @@ module Bridgetown
17
19
  site.in_source_dir(site.config[:partials_dir], "#{partial_name}.serb")
18
20
  ).render(self, options)
19
21
  end
20
-
21
- def markdownify(input = nil, &block)
22
- content = Bridgetown::Utils.reindent_for_markdown(
23
- block.nil? ? input.to_s : capture(&block)
24
- )
25
- converter = site.find_converter_instance(Bridgetown::Converters::Markdown)
26
- converter.convert(content).strip
27
- end
28
22
  end
29
23
 
30
24
  module Converters
@@ -6,51 +6,31 @@ module Serbea
6
6
  Serbea::Pipeline.deny_value_method %i(escape h prepend append assign_to)
7
7
  end
8
8
 
9
- def capture(obj = nil, &block)
9
+ def capture(*args)
10
10
  previous_buffer_state = @_erbout
11
- @_erbout = Serbea::Buffer.new
12
-
13
- # For compatibility with ActionView, not used by Bridgetown normally
14
- previous_ob_state = @output_buffer
15
- @output_buffer = Serbea::Buffer.new
16
-
17
- result = instance_exec(obj, &block)
18
- if @output_buffer != ""
19
- # use Rails' ActionView buffer if present
20
- result = @output_buffer
21
- end
11
+ @_erbout = Serbea::OutputBuffer.new
12
+ result = yield(*args)
22
13
  @_erbout = previous_buffer_state
23
- @output_buffer = previous_ob_state
24
14
 
25
15
  result&.html_safe
26
16
  end
27
-
17
+
28
18
  def pipeline(context, value)
29
19
  Pipeline.new(context, value)
30
20
  end
31
-
21
+
32
22
  def helper(name, &helper_block)
33
23
  self.class.define_method(name) do |*args, &block|
34
24
  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
-
25
+ @_erbout = Serbea::OutputBuffer.new
41
26
  result = helper_block.call(*args, &block)
42
- if @output_buffer != ""
43
- # use Rails' ActionView buffer if present
44
- result = @output_buffer
45
- end
46
27
  @_erbout = previous_buffer_state
47
- @output_buffer = previous_ob_state
48
-
28
+
49
29
  result.is_a?(String) ? result.html_safe : result
50
30
  end
51
31
  end
52
32
  alias_method :macro, :helper
53
-
33
+
54
34
  def h(input)
55
35
  ERB::Util.h(input.to_s)
56
36
  end
@@ -1,12 +1,13 @@
1
1
  require "active_support/core_ext/string/output_safety"
2
+ require "active_support/core_ext/object/blank"
2
3
 
3
4
  module Serbea
4
5
  class Pipeline
5
- def self.exec(template, input: (no_input_passed = true; nil), include_helpers: nil)
6
+ def self.exec(template, locals = {}, include_helpers: nil, **kwargs)
6
7
  anon = Class.new do
7
8
  include Serbea::Helpers
8
9
 
9
- attr_accessor :input, :output
10
+ attr_accessor :output
10
11
  end
11
12
 
12
13
  if include_helpers
@@ -14,12 +15,11 @@ module Serbea
14
15
  end
15
16
 
16
17
  pipeline_obj = anon.new
17
- pipeline_obj.input = input unless no_input_passed
18
18
 
19
19
  full_template = "{{ #{template} | assign_to: :output }}"
20
20
 
21
21
  tmpl = Tilt::SerbeaTemplate.new { full_template }
22
- tmpl.render(pipeline_obj)
22
+ tmpl.render(pipeline_obj, locals.presence || kwargs)
23
23
 
24
24
  pipeline_obj.output
25
25
  end
@@ -54,8 +54,9 @@ module Serbea
54
54
  @value_methods_denylist ||= Set.new
55
55
  end
56
56
 
57
- def initialize(context, value)
58
- @context = context
57
+ def initialize(binding, value)
58
+ @binding = binding
59
+ @context = binding.receiver
59
60
  @value = value
60
61
  end
61
62
 
@@ -83,6 +84,19 @@ module Serbea
83
84
  else
84
85
  @value = @context.send(name, @value, *args)
85
86
  end
87
+ elsif @binding.local_variables.include?(name)
88
+ var = @binding.local_variable_get(name)
89
+ if var.respond_to?(:call)
90
+ unless kwargs.empty?
91
+ @value = var.call(@value, *args, **kwargs)
92
+ else
93
+ @value = var.call(@value, *args)
94
+ end
95
+ else
96
+ "Serbea warning: Filter #{name} does not respond to call".tap do |warning|
97
+ self.class.raise_on_missing_filters ? raise(warning) : STDERR.puts(warning)
98
+ end
99
+ end
86
100
  else
87
101
  "Serbea warning: Filter not found: #{name}".tap do |warning|
88
102
  self.class.raise_on_missing_filters ? raise(warning) : STDERR.puts(warning)
@@ -1,34 +1,51 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # inspired by https://github.com/haml/haml/blob/main/lib/haml/plugin.rb
4
3
  module Serbea
5
- # This module makes Serbea work with Rails using the template handler API.
6
- class Plugin
7
- def handles_encoding?; true; end
8
-
9
- def compile(template, source)
10
- "self.class.include(Serbea::Helpers);" + Tilt::SerbeaTemplate.new { source }.precompiled_template([])
4
+ module Rails
5
+ module FrontmatterHelpers
6
+ def set_page_frontmatter=(data)
7
+ @frontmatter ||= HashWithDotAccess::Hash.new
8
+ @frontmatter.update(data)
9
+ end
11
10
  end
12
11
 
13
- def self.call(template, source = nil)
14
- source ||= template.source
12
+ module FrontmatterControllerActions
13
+ extend ActiveSupport::Concern
14
+
15
+ included do
16
+ Serbea::TemplateEngine.front_matter_preamble = "self.set_page_frontmatter = local_frontmatter = YAML.load"
17
+
18
+ before_action { @frontmatter ||= HashWithDotAccess::Hash.new }
15
19
 
16
- new.compile(template, source)
20
+ helper Serbea::Rails::FrontmatterHelpers
21
+ end
17
22
  end
18
23
 
19
- def self.initialize_frontmatter
20
- if defined?(ApplicationHelper)
21
- Serbea::TemplateEngine.front_matter_preamble = "self.set_page_frontmatter = local_frontmatter = YAML.load"
22
-
23
- ApplicationHelper.define_method(:set_page_frontmatter=) do |data|
24
- @frontmatter ||= HashWithDotAccess::Hash.new
25
- @frontmatter.update(data)
26
- end
27
-
28
- ApplicationController.before_action { @frontmatter ||= HashWithDotAccess::Hash.new }
24
+ class TemplateHandler
25
+ def handles_encoding?; true; end
26
+
27
+ def compile(template, source)
28
+ "self.class.include(Serbea::Helpers);" + Tilt::SerbeaTemplate.new { source }.precompiled_template([])
29
+ end
30
+
31
+ def self.call(template, source = nil)
32
+ source ||= template.source
33
+
34
+ new.compile(template, source)
29
35
  end
30
36
  end
31
37
  end
32
38
  end
33
39
 
34
- ActionView::Template.register_template_handler(:serb, Serbea::Plugin)
40
+ Serbea::TemplateEngine.directive :form, ->(code, buffer) do
41
+ buffer << "{%= form_with model:"
42
+ buffer << code
43
+ buffer << " %}"
44
+ end
45
+ Serbea::TemplateEngine.directive :_, ->(code, buffer) do
46
+ buffer << "{%= content_tag "
47
+ buffer << code
48
+ buffer << " %}"
49
+ end
50
+
51
+ ActionView::Template.register_template_handler(:serb, Serbea::Rails::TemplateHandler)
@@ -1,7 +1,7 @@
1
1
  require "strscan"
2
2
 
3
3
  module Serbea
4
- class Buffer < String
4
+ class OutputBuffer < String
5
5
  def concat_to_s(input)
6
6
  concat input.to_s
7
7
  end
@@ -14,11 +14,42 @@ module Serbea
14
14
  class TemplateEngine < Erubi::Engine
15
15
  FRONT_MATTER_REGEXP = %r!\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)!m.freeze
16
16
 
17
- def self.render_directive=(directive)
18
- @render_directive = directive
17
+ def self.directive(new_directive, directive_resolution)
18
+ directives[new_directive.to_s] = directive_resolution
19
19
  end
20
- def self.render_directive
21
- @render_directive ||= "render"
20
+ def self.directives
21
+ @directives ||= {
22
+ "@" => ->(code, buffer) do
23
+ original_line_length = code.lines.size
24
+
25
+ pieces = code.split(" ")
26
+ if pieces[0].start_with?(/[A-Z]/) # Ruby class name
27
+ pieces[0].prepend " "
28
+ pieces[0] << ".new("
29
+ else # string or something else
30
+ pieces[0].prepend "("
31
+ end
32
+
33
+ includes_block = false
34
+ pieces.reverse.each do |piece|
35
+ if piece == "do" && (pieces.last == "do" || pieces.last.end_with?("|"))
36
+ piece.prepend(") ")
37
+ includes_block = true
38
+ break
39
+ end
40
+ end
41
+
42
+ if includes_block
43
+ buffer << "{%= render#{pieces.join(" ")} %}"
44
+ else
45
+ pieces.last << ")"
46
+ buffer << "{%= render#{pieces.join(" ")} %}"
47
+ end
48
+ (original_line_length - 1).times do
49
+ buffer << "\n{% %}" # preserve original directive line length
50
+ end
51
+ end
52
+ }
22
53
  end
23
54
 
24
55
  def self.front_matter_preamble=(varname)
@@ -125,7 +156,7 @@ module Serbea
125
156
  end
126
157
  end
127
158
 
128
- segments[0] = "pipeline(self, (#{segments[0].strip}))"
159
+ segments[0] = "pipeline(binding, (#{segments[0].strip}))"
129
160
  segments[1..-1].each_with_index do |segment, index|
130
161
  filter, args = segment.strip.match(/([^ :]*)(.*)/m).captures
131
162
  segments[index + 1] = ".filter(:" + filter
@@ -149,39 +180,19 @@ module Serbea
149
180
  string = buff
150
181
  buff = ""
151
182
  until string.empty?
152
- text, code, string = string.partition(/{%@(.*?)%}/m)
153
-
183
+ text, code, string = string.partition(/{%@([a-z_]+)?(.*?)%}/m)
184
+
154
185
  buff << text
155
186
  if code.length > 0
156
- code = $1
187
+ directive = $1
188
+ code = $2
157
189
  unless ["end", ""].include? code.strip
158
- original_line_length = code.lines.size
159
-
160
- pieces = code.split(" ")
161
- if pieces[0].start_with?(/[A-Z]/) # Ruby class name
162
- pieces[0].prepend " "
163
- pieces[0] << ".new("
164
- else # string or something else
165
- pieces[0].prepend "("
166
- end
190
+ directive = $1 ? self.class.directives[$1] : self.class.directives["@"]
167
191
 
168
- includes_block = false
169
- pieces.reverse.each do |piece|
170
- if piece == "do" && (pieces.last == "do" || pieces.last.end_with?("|"))
171
- piece.prepend(") ")
172
- includes_block = true
173
- break
174
- end
175
- end
176
-
177
- if includes_block
178
- buff << "{%= #{self.class.render_directive}#{pieces.join(" ")} %}"
192
+ if directive
193
+ directive.(code, buff)
179
194
  else
180
- pieces.last << ")"
181
- buff << "{%= #{self.class.render_directive}#{pieces.join(" ")} %}"
182
- end
183
- (original_line_length - 1).times do
184
- buff << "\n{% %}" # preserve original directive line length
195
+ raise "Handler for Serbea template directive `#{$1}' not found"
185
196
  end
186
197
  else
187
198
  buff << "{% end %}"
@@ -1,3 +1,3 @@
1
1
  module Serbea
2
- VERSION = "0.8.0"
2
+ VERSION = "0.10.2"
3
3
  end
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
17
17
  spec.require_paths = ["lib"]
18
18
 
19
19
  spec.add_runtime_dependency("activesupport", "~> 6.0")
20
- spec.add_runtime_dependency("erubi", "~> 1.9")
20
+ spec.add_runtime_dependency("erubi", ">= 1.10")
21
21
  spec.add_runtime_dependency("hash_with_dot_access", "~> 1.1")
22
22
  spec.add_runtime_dependency("tilt", "~> 2.0")
23
23
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: serbea
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.10.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-14 00:00:00.000000000 Z
11
+ date: 2020-12-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -28,16 +28,16 @@ dependencies:
28
28
  name: erubi
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '1.9'
33
+ version: '1.10'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '1.9'
40
+ version: '1.10'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: hash_with_dot_access
43
43
  requirement: !ruby/object:Gem::Requirement